这是一个非常简单的示例,可以用来做一些事情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
。(我们也可以编写一个类型类来表达第一个更容易满足的约束。)