haskell - monad bind (>>=) 运算符是否更接近函数组合(链接)或函数应用程序?

标签 haskell functional-programming bind monads category-theory

在许多文章中,我读过那个 monad >>=运算符是一种表示函数组合的方式。但对我来说它更接近某种高级功能应用

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

对于组合,我们有
(.)   :: (b -> c) -> (a -> b) -> a -> c
(>=>) :: Monad m => (a -> m b) -> (b -> m c) -> a -> m c

请说清楚。

最佳答案

显然,>>=不是一种表示功能组合的方式。函数组合只需使用 . .但是,我认为您读过的任何文章也不是这个意思。

他们的意思是“升级”函数组合以直接使用“一元函数”,即 a -> m b 形式的函数.此类函数的技术术语是 Kleisli 箭头,实际上它们可以由 <=< 组成。或 >=> . (或者,您可以使用 Category instance ,然后您也可以使用 .>>> 组合它们。)

然而,谈论箭头/类别往往会让初学者感到困惑,就像 point-free definitions的普通函数往往令人困惑。幸运的是,Haskell 还允许我们以更熟悉的风格表达函数,这种风格专注于函数的结果,而不是将函数本身作为抽象态射†。它是通过 lambda 抽象完成的:而不是

q = h . g . f

你可以写
q = (\x -> (\y -> (\z -> h z) (g y)) (f x))

...当然首选的风格是(这只是 lambda 抽象的语法糖!)‡
q x = let y = f x
          z = g y
      in h z

请注意,在 lambda 表达式中,组合基本上是如何被应用程序替换的:
q = \x -> (\y -> (\z -> h z) $ g y) $ f x

适应 Kleisli 箭头,这意味着代替
q = h <=< g <=< f

你写
q = \x -> (\y -> (\z -> h z) =<< g y) =<< f x

使用翻转运算符或语法糖看起来当然更好:
q x = do y <- f x
         z <- g y
         h z

所以,确实,=<<<=<喜欢 $. .将其称为组合运算符仍然有意义的原因是,除了“应用于值”之外,>>=运算符还对 Kleisli 箭头组合做了一些重要的事情,而函数组合不需要:加入单子(monad)层。

†这个工作的原因是哈斯克cartesian closed category ,特别是 well-pointed category .从广义上讲,在这样的类别中,箭头可以通过将其应用于简单参数值时的所有结果的集合来定义。

‡@adamse 指出 let不是 lambda 抽象的真正语法糖。这在递归定义的情况下尤其重要,您不能直接使用 lambda 编写。但在这种简单的情况下,let确实表现得像 lambdas 的语法糖,就像 do符号是 lambda 和 >>= 的语法糖. (顺便说一句,有一个扩展允许递归 even in do notation ...它通过使用定点组合器来规避 lambda 限制。)

关于haskell - monad bind (>>=) 运算符是否更接近函数组合(链接)或函数应用程序?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/34545818/

相关文章:

haskell - 使用递归方案的核心递归斐波那契

haskell - 没有 (Data.Functor.Classes.Show1 ExprF) 的实例

scala - 理解 IO monad

c++ - 如何为泛型函数定义 std::function?

JavaScript:好的部分 - 第 8 章,function.apply()

haskell - 我可以证明 (forall x.Coercible (a x) (b x)) 意味着 Coercible a b 吗?

haskell - 在 Haskell 中创建多态递归类型

javascript - 按父 ids 对象 Ramda 分组数组

c++ - 使用 boost::function 作为函数参数?

ajax - ajax 加载后绑定(bind)自定义事件处理程序