标准库 Haskell 类型类 MonadPlus
、Alternative
和 Monoid
各自提供两种语义基本相同的方法:
空值:mzero、empty 或 mempty。
将类型类中的值连接在一起的运算符 a -> a -> a:mplus、<|> 或 mappend。
这三个都指定了实例应遵守的这些法律:
mempty `mappend` x = x
x `mappend` mempty = x
因此,似乎这三个类型类都提供了相同的方法。
(Alternative
还提供了 some
和 many
,但它们的默认定义通常就足够了,因此它们对于这个问题来说并不太重要。)
所以,我的疑问是:为什么有这三个极其相似的类?除了它们不同的超类约束之外,它们之间是否有任何真正的区别?
Applicative
和 MonadPlus
似乎完全相同相同(模超类约束)。
ArrowZero
和 ArrowPlus
用于箭头。我的赌注:使类型签名更清晰(这使得不同的超类约束 the 真正不同)。
ArrowZero
和 ArrowPlus
有种类 * -> * -> *
,这意味着您可以将它们传递给箭头类型一次,用于需要将它们用于多种类型的函数,以使用 Monoid
您必须为每个特定实例化一个 Monoid
实例,并且您不能保证它们以类似的方式处理,这些实例可能是不相关的!
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
也不是 Monad
的超类,直到2015. 现在我们终于在 GHC 中将 Applicative
作为 Monad
的超类(如果还没有在语言标准中。)
实际上,Alternative
之于 Applicative
就像 MonadPlus
之于 Monad
。
对于这些我们会得到
empty <*> m = empty
类似于我们在 MonadPlus
中所拥有的,并且存在类似的分布和捕获属性,您至少应该满足其中一个。
不幸的是,即使是 empty <*> m = empty
法律的主张也太强了。例如,它不适用于 Backwards!
当我们查看 MonadPlus 时,empty >>= f = empty 定律几乎是强加给我们的。无论如何,空结构中不能有任何 '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
更强的声明,而 Alternative
又是比 Monoid
更强的声明,而类型的 MonadPlus
和 Alternative
实例应该是相关,Monoid
可能(有时是)完全不同的东西。
mempty `mappend` x ≡ x
。Monad
,它是一个Alternative
,但不是一个MonadPlus
。我asked a question想找到一个具体的例子;如果你知道一个,我很想看到它。MonadPlus
实际上是两个伪装成一个的类,因为大多数人并不关心。mplus
的右参数。使用左捕获我们有:mplus (return a) b >>= k = return a >>= k = k a
使用左分布:mplus (return a) b >>= k = mplus (return a >>= k) (b >>= k) = mplus (k a) (b >>= k)
所以:mplus (k a) (b >>= k) = k a