在许多文章中我都读过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
请澄清.
显然,>>=
这不是表示功能组合的一种方式.功能组合简单完成.
.但是,我认为你读过的任何文章都不是这个意思.
他们的意思是"升级"功能组合以直接使用"monadic functions",即表单的功能a -> m b
.这些功能的技术术语是Kleisli箭头,实际上它们可以用<=<
或组成>=>
.(或者,您可以使用Category
实例,然后您也可以使用.
或组合它们>>>
.)
然而,谈论箭头/类别往往会让初学者感到困惑,就像普通函数的无点定义常常令人困惑一样.幸运的是,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箭头合成的重要位,其中函数合成不需要:加入monadic层.
† 其工作原因是Hask是一个笛卡尔闭合类别,特别是一个明确的类别.在这样的类别中,广义地说,箭头可以在应用于简单参数值时通过其所有结果的集合来定义.
‡ @adamse评论let
对于lambda抽象来说并不是真正的语法糖.这在递归定义的情况下尤为重要,您无法使用lambda直接编写.但是在这样的简单情况下,let
对于lambdas来说,表现就像语法糖,就像do
符号是lambdas和>>=
.的语法糖一样.(顺便说一句,有一个扩展,即使在do
表示法中也允许递归...它通过使用定点组合器来规避lambda限制.)
就像一个例子,考虑一下:
($) :: (a -> b) -> a -> b let g=g in (g $) :: a -> b g :: (a -> b) _____ Functor f => / \ (<$>) :: (a -> b) -> f a -> f b let g=g in (g <$>) :: f a -> f b g :: (a -> b) ___________________ Applicative f => / / \ (<*>) :: f (a -> b) -> f a -> f b let h=h in (h <*>) :: f a -> f b h :: f (a -> b) _____________ Monad m => /.------. \ (=<<) :: (a -> m b) -> m a -> m b let k=k in (k =<<) :: m a -> m b k :: (a -> m b)
所以,是的,每一个(g <$>)
,(h <*>)
或者(k =<<)
,是某种功能应用程序,被提升为Functor,Applicative Functor或Monad"context".并且(g $)
只是常规类型功能的常规应用.
随着仿函数,函数有没有影响f
整体分量的东西.它们严格地在内部工作,不能影响"包装".
使用Applicatives,函数包含在一个包中f
,该包装与一个参数(作为应用程序的一部分)相结合,以产生结果的包装.
使用Monads,函数本身现在可以生成包装结果,从包装参数(作为应用程序的一部分)中以某种方式提取它们的参数.
我们可以看到三个运算符作为函数上的某种标记,就像数学家喜欢写说f'
或f^
或f*
(在Eugenio Moggi的原始作品中(1) f*
正是使用的,表示提升的函数(f =<<)
).
当然,通过推广的功能:: f a -> f b
,我们可以将它们链接起来,因为现在这些类型排成一行.促销是允许组成的.
(1) "计算和单子的概念",Eugenio Moggi,1991年7月.
更多关于组合性,图片:Monads with Join()而不是Bind()
因此,仿函数"神奇地在里面工作""管道"; 适用的是"预先用部件制造的预制管"; 和monads是"我们去建设管网".举例说明: