标准库的Haskell类型类MonadPlusAlternativeMonoid各自提供两种方法,它们的语义基本相同:

  • 一个空值:mzeroemptymempty
  • 将类型类中的值连接在一起的运算符a -> a -> a:mplus<|>mappend

  • 这三个规则均指定了实例应遵循的以下法律:
    mempty `mappend` x = x
    x `mappend` mempty = x
    

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

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

    因此,我的查询是:为什么这三个类非常相似?除了它们不同的父类(super class)约束之外,它们之间是否还有真正的区别?

    最佳答案

    MonadPlusMonoid具有不同的用途。

    在类型Monoid上参数化*

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

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

    但是,MonadPlus不仅指定您具有一个monoidal结构,而且该结构与Monad的工作方式有关,并且该结构不关心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的父类(super class),但很大程度上是由于Haskell 98中对设计人员的压力不同,即使Functor在2015年之前都不是Monad的父类(super class)。现在,我们终于有了Applicative作为父类(super class)GHC中Monad的数量(如果尚未使用语言标准)。

    实际上,AlternativeApplicativeMonadPlusMonad

    对于这些,我们会得到
    empty <*> m = empty
    

    MonadPlus类似,并且存在类似的分布和捕获属性,您至少应满足其中之一。

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

    当我们看MonadPlus时,空>> = f =空定律几乎被强加给我们。无论如何,空结构中不能包含任何“a”来调用函数f

    但是,由于Applicative不是Monad的父类(super class),并且Alternative并非MonadPlus的父类(super class),因此我们分别定义了两个实例。

    而且,即使ApplicativeMonad的父类(super class),您仍然需要MonadPlus类,因为即使我们遵守了
    empty <*> m = empty
    

    这还不足以证明
    empty >>= f = empty
    

    因此,宣称某事是MonadPlus比宣称它是Alternative更强。

    现在,按照约定,给定类型的MonadPlusAlternative应该一致,但是Monoid可能完全不同。

    例如,MonadPlusAlternativeMaybe做明显的事情:
    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 MonadPlusAlternative更具说服力,而Monoid则比MonadPlus更具说服力,并且尽管类型的AlternativeMonoid实例应该相关,但是ojit_code可能(有时是)完全不同。

    10-08 12:35