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

GHC目前实施>>

(>>) :: m a -> m b -> m b
m >> k = m >>= _ -> k

为什么不做以下改为?

(>>) :: m a -> m b -> m b
m >> k = m *> k

现在,我正在思考>>=做些什么*>没有。

但是,所有的东西都是语法的(例如,类型),所以很难理解它为什么不起作用。 也许monad实例做了一些计算,但应用实例没有,但我认为这会打破该类型的语义。

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


根据源代码中的评论,这是为了防止人们在Applicative实例中重写*> as >>意外创建递归绑定。

(>>)        :: forall a b. m a -> m b -> m b
m >> k = m >>= _ -> k -- See Note [Recursive bindings for Applicative/Monad]

该说明说:

注意:Applicative / Monad的递归绑定

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

(>>) :: forall a b. m a -> m b -> m b
(>>) = (*>)

默认。 你可能倾向于改变这一点以反映所提出的建议,但你真的不应该这样做! 为什么? 因为人们倾向于用另一种方式定义这样的实例:特别是,根据(>>)定义Applicative (*>)的实例是完全合法的,这将导致Monad的默认实现的无限循环! 人们在野外这样做。

这变成了一个棘手的错误追踪,而不是消除它在上游的任何地方,保留原始默认值更容易。


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

liftA2 f xs ys = xs >>= x -> ys >>= y -> pure (f x y)

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

xs *> ys = xs >>=  _ -> ys >>= y -> pure y

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

xs >> ys = xs >>=  _ -> ys

正如你所看到的,这只使用一个绑定,而另一个使用两个绑定。 按照单子身份法,它们是等价的,但编译器不知道单子法。 因此,您建议的方法可能会使优化器工作更加困难,甚至可能会阻止它在某些情况下生成最佳代码。

链接地址: http://www.djcxy.com/p/107.html

上一篇: Why is (>>) not defined as (*>)?

下一篇: Does Python have a ternary conditional operator?