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

问题描述 投票:21回答:2

GHC目前实施>>

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

为什么不这样做呢?

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

现在,我在想>>=做的事情*>没有。

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

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

haskell ghc
2个回答
27
投票

根据source code的评论,如果他们在*>实例中将>>重写为Applicative,则可以防止人们意外地创建递归绑定。

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

说明说:

Note: Recursive bindings for Applicative/Monad

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

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

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

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


9
投票

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

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

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

© www.soinside.com 2019 - 2024. All rights reserved.