标准库Haskell类型类MonadPlus
、Alternative
和Monoid
分别提供了两个语义基本相同的方法:
- 空值:
mzero
、empty
或mempty
。 - 将类型类中的值连接在一起的运算符
a -> a -> a
:mplus
、<|>
或mappend
。
所有三个都规定了这些实例应遵守的法律:
mempty `mappend` x = x
x `mappend` mempty = x
因此,似乎这三个类型类都提供了相同的方法。
(Alternative
也提供了some
和many
,但它们的默认定义通常就足够了,所以它们在这个问题上不太重要。
所以,我的问题是:为什么这三个类非常相似?除了它们不同的超类约束之外,它们之间有什么真正的区别吗?
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
直到2015年才成为Monad
的超类。现在我们终于有了Applicative
作为GHC中Monad
的超类(如果还没有语言标准的话)。
实际上,Alternative
是Applicative
MonadPlus
Monad
对于这些,我们会得到
empty <*> m = empty
类似于我们对MonadPlus
所拥有的,并且存在类似的分配和捕获属性,至少您应该满足其中的一个。
不幸的是,即使是empty <*> m = empty
法律也太强硬了。例如,它不适用于向后!
当我们看MonadPlus时,空的>>= f =空的法律几乎是强加给我们的。空结构中不能有任何'a'来调用f
函数。
然而,由于Applicative
不是Monad
的超类,而Alternative
也不是MonadPlus
的超类,我们最终分别定义这两个实例。
此外,即使Applicative
是Monad
的超阶级,你最终也需要MonadPlus
类,因为即使我们服从
empty <*> m = empty
这还不足以证明这一点
empty >>= f = empty
因此,声称某物是MonadPlus
比声称它是Alternative
更强大。
现在,按照惯例,给定类型的MonadPlus
和Alternative
应该一致,但Monoid
可能完全不同。
例如,Maybe
的MonadPlus
和Alternative
做了显而易见的事情:
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
更强的声明,而又是一个比Monoid
更强的声明,虽然一个类型的MonadPlus
和Alternative
实例应该是相关的,但Monoid
可能是(有时是)完全不同的东西。