【问题标题】:Haskell difference between function composition and bindHaskell函数组合和绑定之间的区别
【发布时间】:2020-03-17 16:35:33
【问题描述】:

我无法理解 Dot(函数组合)和 bind (>>=) 之间的区别。

如果我理解的话,这两种方式将函数的先前结果作为新函数。

那么有什么区别呢?

【问题讨论】:

  • (.) 是一个适用于函数类型的运算符,而 (>>=) 是一个重载运算符。它适用于作为 Monad 类型类实例的所有类型,包括函数类型 (->) r。即使使用函数类型,它们也不完全相同。不过我建议你先检查fmap(.)
  • 粗略地说,(.) 构成a -> bb -> c,而>>= 排序适用 M bb -> M c。类型完全不同,尤其是因为 monad M
  • 另一方面,(.)(<=<)(“kleisli 组合”)非常相似,它们之间的差异/类比值得思考
  • @luqui 确实如此,Category 类统一了“可以组合的事物”的一般概念。 (而Arrow 及其子类更进一步。)(注意我提到这些以防 OP 感兴趣,而不是因为我认为你不知道它们 :-))

标签: haskell bind composition


【解决方案1】:

它们完全不同。让我们看看他们的签名:

(.) :: (b -> c) -> (a -> b) -> (a -> c)
(>>=) :: (Monad m) => m a -> (a -> m b) -> m b

正如你所说,函数组合只是一种将一个函数的结果作为参数传递给另一个函数的方法,如下所示:

f = g . h

等价于

f x = g (h x)

您可以将其视为某种“传送器”,您的价值在其中经过多个处理步骤。

但是(>>=) 完全不同。它与 monad 这样的上下文相关,这在某些上下文中类似于某个值(如果您不熟悉,强烈建议阅读上一个链接)。

所以让x 成为上下文中的某个值。我们的上下文将是可空性(Maybe monad),值为2。所以,x = Just 2。例如,我们可以通过从某个关联容器中查找来获取它(这样的操作可能会失败,这就是它是 Maybe Int,而不是 Int 的原因)。

现在我们想将 x 传递给某个仅接受 Int 的算术函数 f 并且可能会失败,因此它的签名如下所示:

f :: Int -> Maybe Int

由于类型不匹配,我们不能只传递我们的值。我们可以解压x 并使用if 处理某些情况,但我们几乎可以在所有其他语言中做到这一点。在haskell中,我们可以使用(>>=):

x >>= f

这允许链接效果:

  • 如果xNothing,那么结果立即是Nothing
  • 否则x 被解包并传递给f

这是operator ?. 的概括,您可以在某些语言中看到:

x = a?.func1()?.func2();

在每个“步骤”检查null,如果命中null,则立即停止,或者在成功的情况下返回值。在 haskell 中它看起来像:

x = a >>= func1 >>= func2

但是,与 monad 绑定是一个更强大的概念,例如,允许您在没有可变性的语言(如 haskell)中模拟有状态计算。

【讨论】:

  • 与 monads 绑定是一个更强大的概念,例如,允许您实现回溯、协程、延续、异常、可撤消的操作、事务性内存。可变状态是有史以来最无聊的例子。
  • @luqui 哇。我在 Haskell 方面没有那么丰富的经验,所以不知道 monad 有那么强大。
【解决方案2】:

(>>=) 是函数应用程序的一种形式。

(>>=)          :: Monad m => m a -> (a -> m b) -> m b
flip ($)       ::              a -> (a ->   b) ->   b

它需要一个值,但会“提取”其中的一部分以应用给定的函数。链接两个函数,例如x >>= f >>= g,要求g 的参数类型与f 的返回类型不同(但类型相同相似) ,与组合不同,组合需要类型完全匹配

return组成,它 真的只是功能应用,但仅限于某些类型的功能。

flip ($)       ::              a -> (a ->   b) ->   b
(>>=) . return :: Monad m =>   a -> (a -> m b) -> m b

(.) 更像(<=<)(来自Control.Monad)。

(.)   ::            (b ->   c) -> (a ->   b) -> a ->   c
(<=<) :: Monad m => (b -> m c) -> (a -> m b) -> a -> m c

但同样,它不是简单地将一个函数的结果传递给另一个函数,而是在执行应用程序之前首先“提取”一个值。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2013-09-20
    • 2017-05-28
    • 2012-11-20
    • 2017-01-20
    • 2011-09-16
    • 2014-01-28
    • 2011-08-18
    • 2011-08-18
    相关资源
    最近更新 更多