どちらも構造をたどり、誘導処理の結果を蓄積するだけです。これには、カタモルフィズムによる反復の一般化が必要です。
> newtype Fix f = Fix {unFix :: f (Fix f)}
> data N a b x = Z a | S b x deriving (Functor)
> type Nat a b = Fix (N a b)
> let z = Fix . Z
> let s x = Fix . S x
> let x = s "blah" $ s "doo" $ s "duh" $ z 0
> let annotate (Z x) = s "annotate" $ z x;
annotate (S x y) = s "annotate" $ s x y
> let exec (Z x) = print x; exec (S x y) = print x >> y
> let cata phi = phi . fmap (cata phi) . unFix
>
> cata exec x
"blah"
"doo"
"duh"
0
>
> cata exec $ cata annotate x
"annotate"
"blah"
"annotate"
"doo"
"annotate"
"duh"
"annotate"
0
コメントにいくつかのリクエストがあり、Fix を使用するとモナドではなくなるのではないかという懸念があったため、何が起こっているのかをさらに詳しく説明しましょう。
関手 G を考える:
G(X) = A + F(G(X))
ここで F は任意の関手です。次に、任意の A に対して不動点を見つけることができます (F と G は明らかに多項式です - 私たちは Hask にいます)。カテゴリのすべてのオブジェクト A をカテゴリのオブジェクトにマップするため、不動点 T(A) の関手について話していることになります。モナドであることがわかります。任意の関手 F のモナドであるため、T(A) は Free Monad です。(以下のコードから明らかに Monad であることがわかります)
{-# LANGUAGE DeriveFunctor
, TypeSynonymInstances #-}
newtype Fix f = Fix {unFix :: f (Fix f)} -- the type of Fixed point of a functor
newtype Compo f g x = Compo {unCompo :: f (g x)} -- composition of functors
instance (Functor f, Functor g) => Functor (Compo f g) where -- composition of functors is a functor
fmap f = Compo . fmap (fmap f) . unCompo
data FreeF a x = Pure a | Free x deriving (Functor) -- it is a bi-functor, really;
-- this derives functor in x
-- a special case of fmap - the fmap with unwrapping; useful to eliminate pattern matching
ffmap :: (a -> b) -> FreeF b a -> b
ffmap f x = case fmap f x of -- unwrapping, since now distinction between Pure and Free is not important
Pure a -> a
Free a -> a
-- Free Monad is a functor of fixed points of functor G(X)
-- G(X) = A + F(G(X))
type Free f a = Fix (Compo (FreeF a) f) -- fixed point of composition F . (FreeF a)
-- unfortunately, when defined as type, (Free f a) cannot be declared
-- as a Monad (Free f) - Haskell wants Free f to be with `a`
-- instance Monad (Free f) where -- this derives a functor in a at the same time;
-- note that fmap will work in x, and is not meant
-- to be equal to (m >>= return . f), which is in `a`
-- return a = Fix $ Compo $ Pure a
-- (Fix (Compo (Pure a))) >>= f = f a
-- (Fix (Compo (Free fx))) >>= f = Fix $ Compo $ Free $ fmap (>>= f) fx
ret :: (Functor f) => a -> Free f a -- yet it is a monad: this is return
ret = Fix . Compo . Pure
-- and this is >>= of the monad
bind :: (Functor f) => Free f a -> (a -> Free f b) -> Free f b
bind (Fix (Compo (Pure a))) f = f a
bind (Fix (Compo (Free fx))) f = Fix $ Compo $ Free $ fmap (`bind` f) fx
-- Free is done
-- here is your functor FooF
data FooF x = Z Int x | S String x deriving (Functor)
type Foo x = Free FooF x
-- catamorphism for an algebra phi "folds" any F(X) (represented by fixed point of F)
-- into X
cata :: (Functor f) => (f x -> x) -> Fix f -> x
cata phi = phi . fmap (cata phi) . unFix
-- helper functions to construct "Foo a"
z :: Int -> Foo a -> Foo a
z x = Fix . Compo . Free . Z x
s :: String -> Foo a -> Foo a
s x = Fix . Compo . Free . S x
tip :: a -> Foo a
tip = ret
program :: Foo (IO ())
program = s "blah" $ s "doo" $ s "duh" $ z 0 $ tip $ return ()
-- This is essentially a catamorphism; I only added a bit of unwrapping
cata' :: (Functor f) => (f a -> a) -> Free f a -> a
cata' phi = ffmap (phi . fmap (cata' phi)) . unCompo . unFix
exec (Z x y) = print x >> y
exec (S x y) = print x >> y
annotate (Z x y) = s "annotated Z" $ z x y
annotate (S x y) = s "met S" $ s x y
main = do
cata' exec program
cata' exec $ cata' annotate (program `bind` (ret . ret))
-- cata' annotate (program >>= return . return)
-- or rather cata' annotate $ fmap return program
program
ですFoo (IO ())
。fmap
in a
(FreeF はバイファンクターであることを思い出してください - fmap in が必要ですa
) にprogram
変換できますFoo (Foo (IO ()))
- 今では annotate のカタモルフィズムは new を構築できFoo (IO ())
ます。
からのものでcata'
あることに注意してください。iter
Control.Monad.Free