为什么(>>)没有定义为(*>)?

Yol*_*Voe 21 haskell ghc

GHC目前实施>>

(>>) :: m a -> m b -> m b
m >> k = m >>= \_ -> k
Run Code Online (Sandbox Code Playgroud)

为什么不这样做呢?

(>>) :: m a -> m b -> m b
m >> k = m *> k
Run Code Online (Sandbox Code Playgroud)

现在,我在想>>=什么*>都没有.

但是所有的东西都是按语法编写的(如在类型方面),所以很难说明为什么它不起作用.也许monad实例做了一些应用实例没有的计算,但我认为这会破坏该类型的语义.

更新我只能选择一个SO答案,但dfeuer答案非常有见地(特别是对于像我这样在Haskell相对缺乏经验的人).

4ca*_*tle 27

根据在注释的源代码,这是为了防止人们意外地创建一个递归的,如果他们重写结合*>作为>>他们的Applicative实例.

(>>)        :: forall a b. m a -> m b -> m b
m >> k = m >>= \_ -> k -- See Note [Recursive bindings for Applicative/Monad]
Run Code Online (Sandbox Code Playgroud)

说明说:

注意:Applicative/Monad的递归绑定

最初的Applicative/Monad提案表明,实施后,(>>)的指定实施将成为

(>>) :: forall a b. m a -> m b -> m b
(>>) = (*>)
Run Code Online (Sandbox Code Playgroud)

默认情况下.你可能倾向于改变这个以反映所陈述的提议,但你真的不应该!为什么?因为人们往往定义此类情况下,其他各地方式:尤其是,这是完全合法的定义一个应用型的实例(*>)来讲(>>),这将导致对单子的默认实现无限循环!人们在野外做这件事.

这变成了一个令人讨厌的错误,它很难追踪,而不是在上游到处消除它,它更容易保留原始默认值.


dfe*_*uer 9

当然,4castle的回答是对的,但还有另外一件事要考虑.并非每个Monad实例都支持比Applicative实例更高效的实例liftA2 = liftM2.那是,

liftA2 f xs ys = xs >>= \x -> ys >>= \y -> pure (f x y)
Run Code Online (Sandbox Code Playgroud)

使用默认值(*>) = liftA2 (flip const)给出

xs *> ys = xs >>= \ _ -> ys >>= \y -> pure y
Run Code Online (Sandbox Code Playgroud)

另一方面,(>>)给出的默认定义

xs >> ys = xs >>= \ _ -> ys
Run Code Online (Sandbox Code Playgroud)

如您所见,这只使用一个绑定,而另一个使用两个绑定.根据monad身份法,它们是等价的,但编译器不知道monad定律.因此,您建议的方法可能会使优化器更加努力,甚至可能会阻止它在某些情况下生成最佳代码.