我读了Why MonadPlus and not Monad + Monoid?,我理解了理论上的差异,但我无法找出实际的区别,因为对于List来说它看起来是一样的。
mappend [1] [2] == [1] <|> [2]
是。也许有不同的实现
mappend (Just "a") (Just "b") /= (Just "a") <|> (Just "b")
但我们可以像Alternative
一样实现Maybe Monoidinstance Monoid (Maybe a) where
Nothing `mappend` m = m
m `mappend` _ = m
那么,是否有人能够展示解释Alternative和Monoid之间实际区别的代码示例?
的重复答案 0 :(得分: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))
。所以让我们将其添加到上下文中:
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
。 (我们可以编写一个表达第一个,更容易满足约束的类型类。)