我对Monads
和Monoids
都很陌生,最近还了解了MonadPlus
。从我看到,Monoid
和MonadPlus
都提供了一个带有关联二进制操作和标识的类型。 (我用数学术语称这是一个半群。)那么Monoid
和MonadPlus
之间有什么区别?
答案 0 :(得分:31)
semigroup是配备有关联二进制运算的结构。 monoid是半群,具有二进制操作的标识元素。
每个monad必须遵守the monad laws。对于我们的案例,重要的是相关性法则。使用>>=
表示:
(m >>= f) >>= g ≡ m >>= (\x -> f x >>= g)
现在让我们应用这个法则来推断>> :: m a -> m b -> m b
:
(m >> n) >> p ≡ (m >>= \_ -> n) >>= \_ -> p
≡ m >>= (\x -> (\_ -> n) x >>= \_ -> p)
≡ m >>= (\x -> n >>= \_ -> p)
≡ m >>= (\x -> n >> p)
≡ m >> (n >> p)
(我们选择了x
,因此它不会出现在m
,n
或p
中。
如果我们将>>
专门化为m a -> m a -> m a
类型(用b
代替a
),我们会看到适用于任何类型a
的操作>>
在m a
上形成半群。由于任何a
都是如此,我们得到一个由a
索引的半群。但是,它们通常不是幺半群 - 我们没有>>
的身份元素。
MonadPlus
又添加了两项操作mplus
和mzero
。 MonadPlus
laws明确声明mplus
和mzero
必须在m a
上为任意a
形成一个幺半群。同样,我们得到了一类由a
索引的幺半群。
请注意MonadPlus
和Monoid
之间的区别:Monoid
表示某些单一类型符合幺半群规则,而MonadPlus
表示所有可能的a
类型m a
满足幺半群定律。这是一个更强大的条件。
因此,MonadPlus
实例形成两种不同的代数结构:一类具有>>
的半群和一类具有mplus
和mzero
的幺半群。 (这不是一种不常见的事情,例如,大于零的自然数集{1,2,...}
与+
和×
以及1
形成一个半群。)
答案 1 :(得分:12)
如果我们MonadPlus m
成立,那么您说m
是Monad
,而是m a
(应用a
产生的类型类型"函数" m
)是一个幺半群。
如果我们定义(类似于Data.Monoid
的定义,但我们稍后会使用它)
class Semigroup a where (<>) :: a -> a -> a
class Semigroup a => Monoid a where zero :: a
然后它有
mzero :: MonadPlus m => m a
mplus :: MonadPlus m => m a -> m a -> m a
具有相当类型和适当的法律
-- left and right identity
mplus a mzero == a
mplus mzero a == a
-- associativity
(a `mplus` b) `mplus` c == a `mplus` (b `mplus` c)
如果我们使用Monoid
-XFlexibleInstances
{-# LANGUAGE FlexibleInstances #-}
instance MonadPlus m => Semigroup (m a) where (<>) = mplus
instance MonadPlus m => Monoid (m a) where zero = mzero
虽然这些与Data.Monoid
中的实例严重重叠,这可能是它不是标准实例的原因。
像这样的幺半群的另一个例子是来自Alternative m => m a
的{{1}}。
答案 2 :(得分:7)
我必须强调非常重要的区别:与Monoid不同,与其他答案不同,MonadPlus 不提供具有关联二进制操作和身份的类型。 Haskell报告,唯一可以声明标准状态的文件,没有规定MonadPlus的法律,因此不要求mplus是关联的或mzero 成为它的左或右单位。也许作者仍在辩论法律:mplus有很好的理由不是联想的。例如,如果mplus是关联的但非交换的,那么由MonadPlus表示的非确定性搜索计算可能不完整(即,存在我们无法找到的解决方案)。由于mplus很少是可交换的,所以如果我们坚持关联性,任何完整的非确定性搜索过程都不能由MonadPlus表示。关于SC的MonadPlus法律问题进行了详细讨论:Must mplus always be associative