MonadPlus 和 Monoid 有不同的用途。
Monoid 的参数化类型为 *。
class Monoid m where
mempty :: m
mappend :: m -> m -> m
因此它几乎可以被实例化为任何类型,只要有一个明显的关联运算符并且有一个单元。
但是,MonadPlus 不仅指定您有一个单曲面结构,而且该结构与 Monad 的工作方式有关,并且该结构不关心monad 中包含的值,这(部分)由 MonadPlus 接受类型为 * -> * 的参数这一事实表明。
class Monad m => MonadPlus m where
mzero :: m a
mplus :: m a -> m a -> m a
除了幺半群定律,我们还有两套潜在的定律可以应用于MonadPlus。可悲的是,社区不同意他们应该是什么。
至少我们知道
mzero >>= k = mzero
但还有另外两个相互竞争的扩展,左(原文如此)分布规律
mplus a b >>= k = mplus (a >>= k) (b >>= k)
和左接法
mplus (return a) b = return a
因此,MonadPlus 的任何实例都应满足这些附加法律中的一项或两项。
那么Alternative呢?
Applicative 是在Monad 之后定义的,逻辑上属于Monad 的超类,但很大程度上是由于Haskell 98 中设计人员面临的不同压力,甚至Functor 也不是@ 的超类987654343@ 直到 2015 年。现在我们终于在 GHC 中将 Applicative 作为 Monad 的超类(如果还没有在语言标准中。)
实际上,Alternative 是 Applicative,MonadPlus 是 Monad。
我们会得到这些
empty <*> m = empty
与MonadPlus 类似,存在类似的分配和捕获属性,您至少应该满足其中一个。
不幸的是,即使empty <*> m = empty 法律的要求也太强了。例如,它不适用于Backwards!
当我们查看 MonadPlus 时,空的 >>= f = empty 定律几乎是强加给我们的。无论如何,空结构中不能有任何'a'来调用函数f。
然而,由于Applicative 不是 Monad 的超类,而Alternative 不是 MonadPlus 的超类,我们最终定义了这两个实例分开。
此外,即使 Applicative 是 Monad 的超类,你最终还是需要 MonadPlus 类,因为即使我们确实遵守了
empty <*> m = empty
这还不足以证明这一点
empty >>= f = empty
因此,声称某事物是MonadPlus 比声称它是Alternative 要强。
现在,按照惯例,给定类型的 MonadPlus 和 Alternative 应该一致,但 Monoid 可能完全不同。
例如,MonadPlus 和 Alternative for Maybe 做显而易见的事情:
instance MonadPlus Maybe where
mzero = Nothing
mplus (Just a) _ = Just a
mplus _ mb = mb
但是Monoid 实例将一个半群提升为Monoid。可悲的是,因为当时在 Haskell 98 中不存在 Semigroup 类,它通过需要 Monoid 来做到这一点,但不使用它的单位。 ಠ_ಠ
instance Monoid a => Monoid (Maybe a) where
mempty = Nothing
mappend (Just a) (Just b) = Just (mappend a b)
mappend Nothing x = x
mappend x Nothing = x
mappend Nothing Nothing = Nothing
TL;DR MonadPlus 是比 Alternative 更强的声明,而 Alternative 又是比 Monoid 更强的声明,而 MonadPlus 和 Alternative 实例类型应该是相关的,Monoid 可能(有时是)完全不同的东西。