Higher-order abstract syntax
Posted: 2016-12-26 , Modified: 2016-12-26
Tags: haskell, functional programming
Posted: 2016-12-26 , Modified: 2016-12-26
Tags: haskell, functional programming
Higher-order abstract syntax (abbreviated HOAS) is a technique for the representation of abstract syntax trees for languages with variable binders. wikipedia
Compared to FOAS (which are basically just normal abstract syntax trees), HOAS exposes additional structure: the relationship between variables and their binding sites.
(What is a binding site?)
In HOAS, let
is a function exp -> (exp -> exp) -> exp
.
let x = 1 + 2 in x + x
becomes
let (1 + 2) (\x -> x + x)
To reason about these expressions as data, limit exp -> exp
to “nice” functions. PHOAS is a solution.
Encoding a language that binds variables in higher order abstract syntax generally involves constructing an abstract data type that contains functions. Rotten bananas h
An exponential functor:
class ExpFunctor f where
xmap :: (a -> b) -> (b -> a) -> f a -> f b
We know from catamorphism: (old
is unFix
)
newtype Nu f = Nu { old :: f (Nu f) }
class Cata f t | t -> f where
cata :: (f a -> a) -> t -> a
instance Functor f => Cata f (Nu f) where
cata f = f . fmap (cata f) . old
Catamorphism for exponential functor:
cataMH :: ExpFunctor f => (f a -> a) -> (a -> f a) -> Nu f -> a
cataMH f g = f . xmap (cataMH f g) (anaMH f g) . old
anaMH :: ExpFunctor f => (f a -> a) -> (a -> f a) -> a -> Nu f
anaMH f g = Nu . xmap (anaMH f g) (cataMH f g) . g
To get a catamorphism for the fixpoint of an exponential functor, you need both a folding and unfolding operation. “To use the Meijer/Hutton catamorphism to write a pretty printer, you have to write a parser as well; to use it to eval, you must also be able to reify values back into programs.”
Actually, you don’t need a full inverse, just a right-inverse such that cata f . place = id
. Put the Place term into an explicit recursion ADT (Roll is just Fix):
data Rec f a = Roll (f (Rec f a)) | Place a
Now we can define a catamorphism:
cataFS :: ExpFunctor f => (f a -> a) -> Rec f a -> a
cataFS f (Roll x) = f (xmap (cataFS f) Place x)
cataFS f (Place x) = x
From Rotten Bananas:
fmap can really only be defined for ‘covariant endofunctors on the category of types’ (?)
Most covariant functors used in Haskell are among the so-called ‘polynomial’ functors, meaning that they can be built up out of sums, products and constants.
That said, polynomial functors are not the only covariant functors, because you can also have some functions in the type, as long as the type over which you are parameterized only occurs in ‘positive’ position. The informal way to think about it is that every time you have a parameter on the left of an (->) in the type, the occurrence switches signs, starting positive, so for some Functors, you can have functions, as long as the parameter occurs only in positive positions.