MonadPlus,Alternative和Monoid类型类之间的区别?


83

标准库的Haskell类型类MonadPlusAlternative以及Monoid各自提供两种方法具有基本相同的语义:

  • 空值:mzeroemptymempty
  • 操作a -> a -> a:在该类型类联接值加在一起mplus<|>mappend

这三个规则均指定了实例应遵循的以下法律:

mempty `mappend` x = x
x `mappend` mempty = x

因此,似乎三个类型类都提供相同的方法。

Alternative也提供somemany,但是它们的默认定义通常就足够了,因此,在这个问题上它们并不是太重要。)

所以,我的查询是:为什么这三个类非常相似?除了它们不同的超类约束之外,它们之间是否还有真正的区别?


这是个好问题。特别是ApplicativeMonadPlus似乎完全相同(模超类约束)。
彼得

1
还有ArrowZeroArrowPlus为箭头。我敢打赌:使类型签名更整洁(这使不同的超类约束成为真正的区别)。
Cat Plus Plus

2
@CatPlusPlus:好,ArrowZero并且ArrowPlus具有kind * -> * -> *,这意味着您可以将它们传递给箭头类型一次,以用于需要将它们用于多种类型的函数,要使用,Monoid您必须Monoid为每个特定项要求一个实例实例化,并且您不能保证它们以类似的方式处理,实例可能是无关的!
爱德华KMETT

Answers:


120

MonadPlusMonoid达到不同的目的。

Monoid通过类型的类型对A进行参数化*

class Monoid m where
    mempty :: m
    mappend :: m -> m -> m

因此,几乎可以将任何类型的实例化,只要有明显的关联运算符并且具有单位的运算符即可。

但是,MonadPlus不仅指定您具有一个单曲面结构,而且该结构与Monad工作方式相关,并且该结构不关心单子中包含的值,事实(部分)表明了这一点。这MonadPlus需要一种争论* -> *

class Monad m => MonadPlus m where
    mzero :: m a
    mplus :: m a -> m a -> m a

除了monoid法则,我们还有两套可能适用的法则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中对设计师的压力不同,甚至直到2015Functor年才成为超类Monad。现在,我们终于在GHC中Applicative成为超类Monad(如果没有,还是语言标准)。

实际上,AlternativeApplicative什么MonadPlusMonad

对于这些,我们会得到

empty <*> m = empty

与我们拥有的类似,MonadPlus并且存在类似的分发和捕获属性,您至少应满足其中之一。

不幸的是,即使empty <*> m = empty法律也没有足够的主张。例如,它不适用于Backwards

当我们查看MonadPlus时,几乎要强制执行空>> = f =空定律。无论如何,空构造不能包含任何“ a”来调用函数f

但是,由于Applicative没有的超类MonadAlternative不是一个超类MonadPlus,我们拉闸分别定义两个实例。

而且,即使Applicative是的超类Monad,您仍然会需要MonadPlus该类,因为即使我们遵守了

empty <*> m = empty

这还不足以证明

empty >>= f = empty

因此,宣称某物为aMonadPlus比声称其为有力Alternative

现在,按照约定,给定类型的MonadPlusAlternative应当达成共识,但Monoid可能完全不同。

例如,MonadPlusand AlternativeforMaybe做显而易见的事情:

instance MonadPlus Maybe where
    mzero = Nothing
    mplus (Just a) _  = Just a
    mplus _        mb = mb

Monoid实例将一个半群提升为Monoid。可悲的是,由于SemigroupHaskell 98当时不存在一个类,因此它通过请求aMonoid而不是使用其单元来实现。ಠ_ಠ

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,而在TL; DR则是有力的主张Monoid,并且虽然类型的MonadPlusAlternative实例应该相关,但Monoid可能(有时是)完全不同。


2
很好的答案,但是最后一个定义似乎是错误的,它不能满足要求mempty `mappend` x ≡ x
Vitus 2012年

2
好答案。没有人知道具有和实现不同 的(常用)类型吗?MonadPlusAlternative
彼得

7
@EdwardKmett:这个答案似乎暗示可能有一个Monadwhich是anAlternative而不是a MonadPlus。我问了一个有关寻找具体例子的问题。如果您知道一个,我很乐意看到它。
Antal Spector-Zabusky

2
您能解释一下monadplus的左捕获法吗?[]显然违反了它;如果[]的第一个参数为非空值,是否应该忽略它的第二个参数?
ben w

4
@benw左派分发可以说是更明智的法律,但在某些情况下并不适用。左渔获是其他情况倾向于支持的另一条法律,但大多数其他情况却不支持。因此,我们实际上有2个在很大程度上由不同实例执行的不相关的法律集,所以MonadPlus实际上将两类伪装成一类是因为大多数人不在乎。
爱德华KMETT
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.