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

00d*_*ani 80 haskell functional-programming typeclass applicative monoids

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

  • 空值:mzero,emptymempty.
  • 操作员a -> a -> a,在类型类联接值加在一起:mplus,<|>mappend.

所有这三个都规定了应遵守的法律:

mempty `mappend` x = x
x `mappend` mempty = x
Run Code Online (Sandbox Code Playgroud)

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

(Alternative也提供somemany,但它们的默认定义通常是足够的,所以它们在这个问题上并不太重要.)

所以,我的疑问是:为什么这三个极为相似的类?除了不同的超类限制之外,它们之间是否有任何真正的区别?

Edw*_*ETT 111

MonadPlusMonoid服务于不同的目的.

A Monoid在一种类型上进行参数化*.

class Monoid m where
    mempty :: m
    mappend :: m -> m -> m
Run Code Online (Sandbox Code Playgroud)

所以它可以被几乎任何类型实例化,其中有一个明显的操作符是关联的并且有一个单元.

但是,MonadPlus不仅指定您具有幺半群结构,而且该结构与Monad工作方式有关,并且该结构不关心monad中包含的值,这是(部分)由事实指示的这MonadPlus是一种善意的争论* -> *.

class Monad m => MonadPlus m where
    mzero :: m a
    mplus :: m a -> m a -> m a
Run Code Online (Sandbox Code Playgroud)

除了幺半群法律,我们还有两套可能适用的法律MonadPlus.可悲的是,社区不同意他们应该是什么.

至少我们知道

mzero >>= k = mzero
Run Code Online (Sandbox Code Playgroud)

但还有另外两个竞争扩展,左(sic)分布法

mplus a b >>= k = mplus (a >>= k) (b >>= k)
Run Code Online (Sandbox Code Playgroud)

和左捕法

mplus (return a) b = return a
Run Code Online (Sandbox Code Playgroud)

因此,任何实例都MonadPlus应该满足这些附加法律中的一个或两个.

那怎么样Alternative

Applicative之后被定义Monad,并且在逻辑上属于超类Monad,但很大程度上是由于Haskell 98中设计师的压力不同,甚至FunctorMonad2015年之前都不是超类.现在我们终于拥有ApplicativeMonadGHC 的超类(如果不是但是在语言标准中.)

实际上,AlternativeApplicative什么MonadPlusMonad.

对于这些,我们得到

empty <*> m = empty
Run Code Online (Sandbox Code Playgroud)

类似于我们所拥有的,MonadPlus并且存在类似的分配和捕获属性,其中至少有一个是您应该满足的.

不幸的是,即使是empty <*> m = empty法律也是如此强烈.例如,它不适用于Backwards!

当我们看看MonadPlus时,空的>> = f =空法几乎被强加给我们.无论如何,空构造中不能有任何"a"来调用该函数f.

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

而且,即使Applicative是超级课程Monad,你最终还是需要MonadPlus上课,因为即使我们服从了

empty <*> m = empty
Run Code Online (Sandbox Code Playgroud)

这并不足以证明这一点

empty >>= f = empty
Run Code Online (Sandbox Code Playgroud)

因此声称某事物是一件事,MonadPlus比宣称事情要强Alternative.

现在,按照惯例,MonadPlusAlternative对于给定的类型应该一致,但Monoid可能是完全不同的.

例如MonadPlus,Alternative为了Maybe做明显的事情:

instance MonadPlus Maybe where
    mzero = Nothing
    mplus (Just a) _  = Just a
    mplus _        mb = mb
Run Code Online (Sandbox Code Playgroud)

Monoid实例将一个半群提升为一个半群Monoid.遗憾的是,因为Semigroup当时在Haskell 98中没有一个类,它通过请求a 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
Run Code Online (Sandbox Code Playgroud)

TL; DR MonadPlus是一个更强有力的主张Alternative,反过来比一个更强的主张Monoid,而一个类型的MonadPlusAlternative实例应该相关,Monoid可能(有时是)完全不同的东西.

  • @EdwardKmett:这个答案似乎暗示可能有一个`Monad`是一个'Alternative`而不是一个'MonadPlus`.我[问了一个问题](http://stackoverflow.com/q/13122489/237428)关于找到一个具体的例子; 如果你知道一个,我很乐意看到它. (7认同)
  • @benw左派分布可以说是更明智的法则,但它并不适用于某些情况.左捕获是其他实例倾向于支持的替代法则,但大多数其他实例都不支持.因此,我们确实有两组基本上不相关的法律由不同的实例实施,因此"MonadPlus"实际上是伪装成一类的两类,因为大多数人并不关心. (4认同)
  • 优秀的答案,但最后的定义似乎是错误的,它不满足`mempty \`mappend \`x≡x`. (2认同)
  • 很好的答案.有没有人知道具有*不同*`MonadPlus`和`Alternative`实现的(常用)类型? (2认同)
  • 你能解释一下monadplus的左捕法吗?它显然违反了[]; 如果第一个非空,应该[]真的忽略它的第二个参数吗? (2认同)
  • @EdwardKmett:看起来像monad plus满足左捕和左分布定律必须忽略`mplus`的正确参数.使用左捕获我们有:`mplus(返回a)b >> = k =返回a >> = k = ka`使用左分布:`mplus(返回a)b >> = k = mplus(返回>> = k)(b >> = k)= mplus(ka)(b >> = k)`所以:`mplus(ka)(b >> = k)= ka` (2认同)