在许多文章中我都读到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
请澄清。
答案 0 :(得分:16)
显然,>>=
不是表示功能组合的方法。使用.
完成功能组合。但是,我不认为你读过的任何文章都是这个。
他们的意思是“升级”功能组合以直接使用“monadic functions”,即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
所以,=<<
确实是<=<
,$
就是.
。>>=
。将它称为组合运算符仍然有意义的原因是,除了“应用于值”之外,let
运算符还可以完成关于Kleisli箭头组合的重要位置,该函数组合不需要:加入monadic层。
† 这样做的原因是 Hask 是cartesian closed category,特别是well-pointed category。在这样的类别中,广义上说,箭头可以在应用于简单参数值时通过其所有结果的集合来定义。
‡ @adamse评论let
并不是lambda抽象的语法糖。这在递归定义的情况下尤为重要,您无法直接使用lambda编写。但是在这样的简单情况下,do
的行为就像lambdas的语法糖一样,就像>>=
符号是lambdas的语法糖和Protocol SuperProtocolTest {
var stringOne:String?
var stringTwo:String?
}
一样。 (顺便说一句,那里有一个允许递归even in do
notation的扩展......它通过使用定点组合器来规避lambda限制。)
答案 1 :(得分:10)
就像一个例子,考虑一下:
(g <$>)
所以,其中每一个,(h <*>)
,(k =<<)
或(g $)
都是某种功能应用程序,被提升为Functor,Applicative Functor或Monad&# 34;上下文&#34 ;. f
只是常规函数的常规应用。
使用Functors,函数不会影响整个事物的f
组件。它们严格在内部工作,不会影响&#34;包装&#34; 。
使用Applicatives,函数包含在f'
中,该包装与参数(作为应用程序的一部分)相结合,以产生结果的包装。
使用Monads,函数本身现在可以生成包装结果,从包装参数(作为应用程序的一部分)中以某种方式提取它们的参数。
我们可以看到三个运算符作为函数上的某种标记,比如数学家喜欢写f^
或f*
或f*
(以及Eugenio的原创作品) Moggi (1) (f =<<)
正是使用的,表示提升的函数:: f a -> f b
)。
当然,通过推广的函数{{1}},我们可以将它们链接起来,因为现在这些类型排成一行。促销是允许合成的。
(1)&#34;计算和monad的概念&#34;,Eugenio Moggi,1991年7月。
所以这个仿函数是神奇地在里面工作&#34; &#34;管道&#34 ;;应用是预先用组件制造的预制管道&#34 ;;我们去的时候,monad正在建设管道网络。举例说明: