でできることの非常に簡単な例を次に示します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
ます。(最初の、より簡単に満たすことができる制約を表す型クラスを書くこともできます。)