假设我们有g :: a -> b
和f :: b -> c
。我们可以写:
f . g :: a -> c
。如果我们的函数返回monadic值(即上下文中的值),例如g1 :: (Monad m) => a -> m b
和f1 :: (Monad m) => b -> m c
。我们可以写:
f1 <=< g1 :: (Monad m) => a -> m c
。return x >>= g1 >>= f1
,其中x :: a
,以获取值。甚至是lambda \x -> return x >>= g1 >>= f1
。在语法方面,似乎<=<
与.
更为平行。 <=<
让您更容易理解Monad
只是关于保留上下文的函数组合。为什么>>=
比<=<
更常被讨论?
答案 0 :(得分:9)
<=<
是解释monad laws的好方法:
f <=< return = f -- right identity
return <=< g = g -- left identity
f <=< (g <=< h) = (f <=< g) <=< h -- associativity
它对于展示Kleisli箭的类别非常有用:
newtype Kleisli m a b = Kleisli { runKleisli :: a -> m b }
instance Monad m => Category (Kleisli m) where
Kleisli f . Kleisli g = Kleisli (f <=< g)
id = Kleisli return
你会看到它出现在无点程序中。就个人而言,我也喜欢它的同伴=<<
。
虽然它更容易讨论monad法则和组合,但我认为仍然有一些强烈的教学理由{@ 1}}在monad教程和Haskell介绍中是首选。
第一个原因是>>=
的强大功能是无点代码,并且对于大多数情况来说无点代码对来自C语法家族中的语言的人来说更难(C,C ++ ,Java,Python等)首先要了解。
如果“无点”是一个不熟悉的形容词,这里有三个相同功能的实现:
<=<
它们都运行相同的计算,但最后一个是所谓的无点样式,其中左侧的变量已通过eta conversion删除。
这个例子非常简单,但是无点样式很诱人,很容易导致初学者难以理解的代码。
另一个原因是初学者提出的一个近乎普遍的问题是
“我如何打开f a b = a + b * 2
f a = (a +) . (* 2)
f = flip (.) (*2) . (+)
来获取IO String
?”第一次面对时
Haskell的String
monad。答案当然是,“你没有,你把其余部分联系起来
使用IO
“计算,>>=
可以很容易地解释这种关系
之间
>>=
和
putStrLn "Your first name: " >>= \_ ->
getLine >>= \first ->
putStrLn "Your last name: " >>= \_ ->
getLine >>= \last ->
putStrLn ("Hello " ++ first ++ " " ++ last)
当然,最后一个原因是do
putStrLn "Your first name: "
first <- getLine
putStrLn "Your last name: "
last <- getLine
putStrLn ("Hello " ++ first ++ " " ++ last)
在>>=
的定义中,并且
Monad
不是,而这正是语言被定义的方式。人们是
更有可能在谈论类型成员而不是任意函数时
教别人关于类型类,特别是老师的时候
相对较新的主题本身(正如许多monad教程作者所做的那样)。
答案 1 :(得分:1)
虽然<=<
对于许多应用程序来说更自然,但实际上>>=
作为Monad
类的主要功能有很好的实际原因。原因是,无论何时尝试撰写函数f :: a -> m b
和g :: b -> m c
,您总是需要以某种方式解开f
的结果,这正是>>=
所做的!
比较Maybe
m >>= f = case m of
Nothing -> Nothing
Just a -> f a
f >=> g = \a -> case f a of
Nothing -> Nothing
Just b -> g b
尝试为>=>
提供一个更简单的定义,这真的不可能!
一般来说,查看>>=
和>=>
的默认定义,我们会看到:
(>>= f) = id >=> f
f >=> g = \a -> f a >>= g
-- equivalently = (>>= g) . (>>= f) . return
似乎>=>
正在做更多工作&#34;比>>=
,所以使用最简单的行为描述来定义Monad是明智的。
考虑这一点的理论方法是\m -> m >>= f
正是id >=> f
。我们所说的是>=>
由其对身份的行为唯一定义。
(有人可能认为=<<
更好,因为它与$
,<$>
和<*>
等函数应用的正常方向相匹配,但它并不是真的物质)