Wed 26 Mar 2008
You may recall the definition for an exponential functor from my previous entry, which can also be viewed, I suppose, as functors in the
category of right-invertible functions in Haskell.
class ExpFunctor f where xmap :: (a -> b) -> (b -> a) -> f a -> f b
Clarifying the above, an instance of ExpFunctor should satisfy the slightly generalized version of the Functor laws from Control.Monad:
Since we like to apply xmap to a pair of functions such that f . g = id, as in the Fegaras/Sheard case, we get:
xmap f g . xmap g f = xmap (f . g) (f . g) -- by second xmap law = xmap id id -- by f . g = id = id -- by first xmap law
In any event, what I thought what I'd do today is note that Wouter Swierstra's Data Types a la Carte approach works over exponential functors.
Swierstra's main definition looks something like:
data (f :+: g) e = Inl (f e) | Inr (g e) instance (Functor f, Functor g) => Functor (f :+: g) where fmap f (Inl e) = Inl (fmap f e) fmap f (Inr e) = Inr (fmap f e)
This permits the obvious analogue:
instance (ExpFunctor f, ExpFunctor g) => ExpFunctor (f :+: g) where xmap f g (Inl e) = Inl (xmap f g e) xmap f g (Inr e) = Inr (xmap f g e)
With this we can quickly encode the untyped lambda calculus:
newtype Lam a = Lam (a -> a) instance ExpFunctor Lam where xmap f g (Lam k) = Lam (f . k . g) data App a = App a a instance Functor App where fmap f (App a b) = App (f a) (f b) instance ExpFunctor App where xmap = const . fmap
(Even better we could define a generic Bind and Binary functors, and newtype to get Lam and App)
and we can encode the recursion using any of the ways we previously established to tie the knot (Nu f, ForAll (Rec f), ForAll (Elim f)).
The rest of the a la carte stuff remains unchanged.
We then get a definition of catamorphism 'for free', due to the definitions from the other day.
From there we can define a fairly generic pretty printing framework.
class ShowAlgebra f where showAlgebra :: f ([String] -> String) -> [String] -> String instance (ShowAlgebra f, ShowAlgebra g) => ShowAlgebra (f :+: g) where showAlgebra (Inl e) = showAlgebra e showAlgebra (Inr e) = showAlgebra e instance (Cata f (ForAll t), ShowAlgebra f) => Show (ForAll t) where show x = cata showAlgebra (runForAll x) vars
And then we can derive particular instances for the untyped lambda calculus we slapped together above:
instance ShowAlgebra Lam where showAlgebra (Lam k) (v:vars) = "(\\\\" ++ v ++ ". " ++ k (const v) vars ++ ")" instance ShowAlgebra App where showAlgebra (App a b) vars = "(" ++ a vars ++ " " ++ b vars ++ ")"
Then using the combinators from the other day:
type Term a = Elim (Lam :+: App) a type Expr = ForAll Term app_id_id :: Term a app_id_id = app (lam id) (lam id) -- for suitable definitions of app and lam a la the a la Carte paper. main = putStrLn . show $ safe app_id_id
Other algebra structures can be derived similarly.