我们可以用Alternative做什么,但用Monoid做不了呢?

9

我阅读了为什么使用MonadPlus而非Monad + Monoid?,我理解它们在理论上有区别,但是对于List这类数据结构来说,它们看起来相同,我无法找到实际的区别。

mappend [1] [2] == [1] <|> [2]

是的,可能有不同的实现方式。

mappend (Just "a") (Just "b") /= (Just "a") <|> (Just "b")

但我们可以像实现Alternative一样实现Maybe Monoid。

instance Monoid (Maybe a) where
  Nothing `mappend` m = m
  m `mappend` _ = m

所以,有人可以展示一个实际说明Alternative和Monoid之间差异的代码示例吗?
这个问题不是为什么是MonadPlus而不是Monad + Monoid?的重复。

2
啊...抱歉,但这仍然是一个重复的问题。Toxaris 已经很清楚地列出,您不能用Monoid做什么,尤其是第3点和第4点: “3. 如果我们想要使用无限多个不同的 a.MonadPlus m => ... 而不是不可能。 4. 如果我们不知道需要什么 aMonadPlus m => ... 而不是不可能。”如果有关此事的任何疑问,请具体提出。 - leftaroundabout
1
这只是理论解释。即使在第一条评论中也提到了“我真的很想在这里看到一个具体的例子”。我想要实际的代码示例,可以展示3和4如何影响单子使用。 - ais
1
好的,我明白你的意思。但我认为编造一些牵强的例子并不有帮助——当然,在这里我们可以给出一些小例子,比如说“现在我们为什么不抽象地暴露那个私有数据类型”或者“这些是一些Monoid(f A), Monoid(f B)的约束条件,但又怎样呢?——看起来并不那么糟糕”或者“为什么不将那些无限多/未知的类型放入一个存在/GADT包装器中”。但是所有这些解决方法在一个大项目中并不实用。只需查找一些具有Alternative上下文的库函数,并尝试使用Monoid编写它们即可。 - leftaroundabout
1
你是否删除了已关闭的问题并重新发布了它?请避免这样做,因为这会让人感到困惑。 - duplode
2
@ais 请不要因为一个问题被关闭而删除它。相反,可以联合起来争取重新开放它,或者提出一个新的问题(就像你在这里所做的),并参考它,解释“确切地”新问题有何不同。我认为你已经很好地解释了这个问题的不同之处。但是隐藏问题历史有点不诚实,并可能引起人们的反感。 - Daniel Wagner
显示剩余3条评论
1个回答

15

这是使用Alternative可以完成的非常简单的示例:

import Control.Applicative
import Data.Foldable

data Nested f a = Leaf a | Branch (Nested f (f a))

flatten :: (Foldable f, Alternative f) => Nested f a -> f a
flatten (Leaf x) = pure x
flatten (Branch b) = asum (flatten b)

现在让我们尝试使用 Monoid 来做同样的事情:

flattenMonoid :: (Foldable f, Applicative f) => Nested f a -> f a
flattenMonoid (Leaf x) = pure x
flattenMonoid (Branch b) = fold (flattenMonoid b)

当然,这段代码无法编译通过,因为在fold (flattenMonoid b)中,我们需要知道展平操作产生的容器元素是Monoid的实例。因此,让我们将其添加到上下文中:

flattenMonoid :: (Foldable f, Applicative f, Monoid (f a)) => Nested f a -> f a
flattenMonoid (Leaf x) = pure x
flattenMonoid (Branch b) = fold (flattenMonoid b)
啊,但现在我们有一个问题,因为我们无法满足递归调用的上下文要求Monoid (f (f a))。那么让我们将其添加到上下文中:

啊,但是现在我们面临一个问题,因为我们无法满足递归调用的上下文要求Monoid (f (f a))。因此,让我们将其添加到上下文中:

flattenMonoid :: (Foldable f, Applicative f, Monoid (f a), Monoid (f (f a))) => Nested f a -> f a
flattenMonoid (Leaf x) = pure x
flattenMonoid (Branch b) = fold (flattenMonoid b)

那就更糟糕了,因为现在递归调用需要更多的东西,即 Monoid (f (f (f a)))...

如果我们可以这样写就太好了:

flattenMonoid :: ((forall a. Monoid a => Monoid (f a)), Foldable f, Applicative f, Monoid (f a)) => Nested f a -> f a

甚至只是

flattenMonoid :: ((forall a. Monoid (f a)), Foldable f, Applicative f) => Nested f a -> f a

我们可以这样做:不用写forall a. Monoid (f a),而是写Alternative f。(我们也可以编写一个表达第一个更容易满足约束条件的类型类。)


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接