当前位置: 首页 > 知识库问答 >
问题:

为什么是MonadPlus而不是Monad Monoid?

岳均
2023-03-14

我试图理解MonadPlus背后的动机。如果已经存在类型类MonadMonoid,为什么有必要呢?

诚然,Monoid实例是具体的类型,而Monad的实例需要一个类型参数。(有关有用的解释,请参见Monoid vs MonadPlus。)但是你不能重写任何类型的约束吗

(MonadPlus m) => ...

作为MonadMonoid的组合?

(Monad m, Monoid (m a)) => ...

控件获取防护功能。例如Monad。它的实施是:

guard :: (MonadPlus m) => Bool -> m ()
guard True = return ()
guard False = mzero

我只能使用MonadMonoid实现它:

guard' :: (Monad m, Monoid (m ())) => Bool -> m ()
guard' True = return ()
guard' False = mempty

有人能澄清MonadPlusMonadMonid之间的真正区别吗?

共有3个答案

逑景铄
2023-03-14

通过量化约束语言扩展,您可以表示幺半群(ma)实例必须在a的所有选择中保持一致:

{-# LANGUAGE QuantifiedConstraints #-}

class (Monad m, forall a. Monoid (m a)) => MonadPlus m

mzero :: (MonadPlus m) => m a
mzero = mempty

mplus :: (MonadPlus m) => m a -> m a -> m a
mplus = mappend

Alternatively,我们可以为所有这样的幺半群单子实现“real”MonadPlus类:

{-# LANGUAGE GeneralizedNewtypeDeriving, DerivingStrategies, QuantifiedConstraints #-}
{-# LANGUAGE UndecidableInstances #-}

import Control.Monad
import Control.Applicative

newtype MonoidMonad m a = MonoidMonad{ runMonoidMonad :: m a }
    deriving (Functor, Applicative, Monad)

instance (Applicative m, forall a. Monoid (m a)) => Alternative (MonoidMonad m) where
    empty = MonoidMonad mempty
    (MonoidMonad x) <|> (MonoidMonad y) = MonoidMonad (x <> y)

instance (Monad m, forall a. Monoid (m a)) => MonadPlus (MonoidMonad m)

请注意,根据您选择的m,这可能会或可能不会为您提供所需的MonadPlus;例如,MonoidMonad[]实际上与[]相同;但是对于MaybeMonoid实例通过人为地给它一个标识元素来提升一些基础半群,而MonadPlus实例是左偏选择;所以我们必须首先使用MonoidMonad而不是MonoidMonad,也许来获得正确的实例。

池阳伯
2023-03-14

你的guard'与你的Monoid MA类型不匹配。

如果你的意思是Monoid(ma),那么你需要定义mempty对于m()是什么。完成后,就定义了一个MonadPlus

换句话说,MonadPlus定义了两个操作部分:mzeromplus满足两个规则:mzero相对于mplus是中性的,mplus是关联的。这满足了幺半群的定义,因此mzeromemptymplusmappend

不同之处在于,MonadPlus m对于任何a都是一个幺半群ma,但是monoid m只为m定义了一个幺半群。你的guard'之所以有效,是因为你只需要m就可以成为Monoid,只用于()。但是MonadPlus更强,它声称ma是任何a的幺半群。

邓才
2023-03-14

但是你不能重写任何类型的约束吗

(MonadPlus m) => ...

作为单子和幺半群的组合?

不。在你链接的问题的顶部答案中,已经有一个很好的解释了MonadPlus和Monoid的定律。但是,即使我们忽略了类型类法则,也存在差异。

幺半群(ma)=

>

  • 如果我们不想告诉调用者我们打算使用什么a
    MonadPlus m=

    如果我们想使用多个不同的a
    MonadPlus m=

    如果我们想使用无限多个不同的a
    MonadPlus m=

    如果我们不知道我们需要什么<代码>MonadPlus m=

  •  类似资料:
    • 做对了"。事实证明,可以用来表示任何类似ListT-dod-right的东西。引用加布里埃尔·冈萨雷斯的话: 请注意,您可以仅使用变压器依赖项构建任何(不仅仅是管道中的那个)。例如,下面是如何实现: 这将在那里键入check作为any,并为所有人做正确的事情。 所以我的问题是:对于管道的消费者部分,是否有一个对偶到和到? 要求: 从不使用,只返回(或从不返回),但使用的管道可以表示为“双重到列表”

    • 在他对问题“类型类,和之间的区别”的回答中,Edward Kmett说 此外,即使是的一个超类,您最终还是需要类,因为遵守 严格来说还不足以证明这一点 因此,声称某样东西是一个比声称它是更强。 很明显,任何不是单子的应用函子都会自动成为的一个例子,它不是,但爱德华·科米特的回答暗示存在一个单子,它是,而不是:它的和

    • 问题内容: 这一直使我感到困惑。看起来这样会更好: 比这个: 是否有特定原因? 问题答案: 这是因为任何可迭代对象都可以连接(例如,列表,元组,字典,集合),但是其内容和“连接器” 必须是 字符串。 例如: 使用字符串以外的其他东西会引发以下错误:

    • 我的讲师在课堂上问过我这个问题,我想知道为什么是宏而不是函数?

    • rank ▲ ✰ vote url 37 511 93 416 url 为什么是string.join(list)而不是list.join(string)? 我一直被这个问题困扰.如果这样写更好: my_list = ["Hello", "world"] print my_list.join("-") # Produce: "Hello-world" 而不是: my_list = ["Hello

    • 问题内容: 我已经在Android代码中使用FloatBuffers一段时间了(从一些opengles教程中复制了它),但是我无法确切地理解此构造是什么以及为什么需要它。 例如,我在许多人的代码和android教程中看到了以下代码(或类似代码): 就我所知,这似乎是冗长和混乱的,我只是说它们只是一个浮点数的包装而已。 问题: 与任何其他类型的float集合或简单数组相反,这种类型的类(ByteBu