这是一个替代答案,因为最近提出了一个类似的问题并将其标记为重复。事实证明,根本不清楚(<<)
应该是什么定义! 虽然在对旧答案的评论中提到了这个问题,但我认为这里没有完全清楚地表明存在重大问题。
显然,定义的两种合理可能性是:
(<<) :: Monad m => m a -> m b -> m a
p << q = do {x <- p; q; return x} -- definition #1
p << q = do {q; p} -- definition #2
类比应用运算符(<*)
and (*>)
,很明显 new(<<)
运算符应该保留从左到右的副作用顺序,并且只具有切换使用哪个动作的返回值的效果,因此定义 #1 显然是正确的。这具有理想的属性,<<
并且<*
将是(行为良好的)monad 的同义词,就像>>
和*>
是同义词一样,因此不足为奇。
当然,与=<<
and类比>>=
,很明显翻转大于号的方向应该具有翻转参数的效果,因此定义 #2 显然是正确的。这具有理想的特性,即一元操作的管道:
u >>= v >>= w >> x >>= y
可以通过翻转运算符来反转:
y =<< x << w =<< v =<< u
这也保留了 Kleisli 运算符的身份:
(f >=> g) x === f x >>= g
(f <=< g) x === f =<< g x
这当然看起来他们应该持有。
无论如何,我不知道这是否是原来的原因(<<)
被遗漏了。(可能不会,因为该决定会早于应用运算符的引入,所以人们会认为“定义#2”是唯一的可能性),但我很确定现在这将是一个症结所在,因为不同的行为考虑到人们期望 applicative(<<)
和(<*)
monad 操作之间的紧密联系,这将是非常出乎意料的。