ChatGPT解决这个技术问题 Extra ChatGPT

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

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

空值:mzero、empty 或 mempty。

将类型类中的值连接在一起的运算符 a -> a -> a:mplus、<|> 或 mappend。

这三个都指定了实例应遵守的这些法律:

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

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

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

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

这是个好问题。特别是,ApplicativeMonadPlus 似乎完全相同相同(模超类约束)。
还有 ArrowZeroArrowPlus 用于箭头。我的赌注:使类型签名更清晰(这使得不同的超类约束 the 真正不同)。
@CatPlusPlus:嗯,ArrowZeroArrowPlus 有种类 * -> * -> *,这意味着您可以将它们传递给箭头类型一次,用于需要将它们用于多种类型的函数,以使用 Monoid您必须为每个特定实例化一个 Monoid 实例,并且您不能保证它们以类似的方式处理,这些实例可能是不相关的!

c
cmaher

MonadPlusMonoid 有不同的用途。

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 的超类不是,我们最终分别定义这两个实例。

此外,即使 ApplicativeMonad 的超类,你最终还是需要 MonadPlus 类,因为即使我们确实遵守了

empty <*> m = empty

这还不足以证明

empty >>= f = empty

因此,声称某事物是 MonadPlus 比声称它是 Alternative 要强。

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

例如,MaybeMonadPlusAlternative 做了显而易见的事情:

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 更强的声明,而类型的 MonadPlusAlternative 实例应该是相关,Monoid 可能(有时是)完全不同的东西。


很好的答案,但是最后一个定义似乎是错误的,它不满足 mempty `mappend` x ≡ x
@EdwardKmett:这个答案似乎暗示可能有一个Monad,它是一个Alternative,但不是一个MonadPlus。我asked a question想找到一个具体的例子;如果你知道一个,我很想看到它。
你能解释一下 monadplus 的左捕获法则吗? []显然违反了它;如果它的第一个参数是非空的, [] 真的应该忽略它的第二个参数吗?
@benw 左分布可以说是更明智的法则,但在某些情况下并不成立。 left catch 是其他情况倾向于支持但大多数其他情况不支持的替代法则。因此,我们确实有 2 组基本不相关的法律正在由不同的实例实施,因此 MonadPlus 实际上是两个伪装成一个的类,因为大多数人并不关心。
@EdwardKmett:似乎满足左捕获和左分布规律的单子加号必须忽略 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