A Tutorial on Church Representations
I’ve written a few times about church representations, but never aimed at someone who’d never heard of what a church representation is. In fact, it doesn’t really seem like too many people have!
In this post I’d like to fix that :)
What is a Church Representation
Simply put, a church representation (CR) is a way of representing a piece of concrete data with a function. The CR can be used through an identical way to the concrete data, but it’s comprised entirely of functions.
They where originally described by Alanzo Church as a way of modeling all data in lambda calculus, where all we have is functions.
Tuples
The simplest CR I’ve found is that of a tuples.
Let’s first look at our basic tuple API
Now this is trivially implemented with (,)
The church representation preserves the interface, but changes all the underlying implementations.
There’s our church pair, notice that it’s only comprised of ->
. It also makes use of higher rank types. This means that a Tuple a b
can be applied to function producing any c
and it must return something of that type.
Let’s look at how the rest of our API is implemented
And that’s it!
It’s helpful to step through some reductions here
And for snd
snd (mkTuple True False)
fst (\f -> f True False)
(\f -> f True False) (\_ b -> b)
(\_ b -> b) True false
False
So we can see that these are clearly morally equivalent. The only real question here is whether, for each CR tuple there exists a normal tuple. This isn’t immediately apparent since the function type for the CR looks a lot more general. In fact, the key to this proof lies in the forall c
part, this extra polymorphism let’s us use a powerful technique called “parametricity” to prove that they’re equivalent.
I won’t actually go into such a proof now since it’s not entirely relevant, but it’s worth noting that both (,)
and Tuple
are completely isomorphic.
To convert between them is pretty straightforward
isoL :: Tuple a b -> (a, b)
isoL tup = tup (,)
isoR :: (a, b) -> Tuple a b
isoR (a, b) = \f -> f a b
Now that we have an idea of how to church representations “work” let’s go through a few more examples to start to see a pattern.
Booleans
Booleans have the simplest API of all
We can build all other boolean operations on test
This API is quite simple to implement with Bool
,
But how could we represent this with functions? The answer stems from test
,
Clever readers will notice this is almost identical to test
, a boolean get’s two arguments and returns one or the other.
We can write an isomorphism between Bool
and Boolean
as well
isoL :: Bool -> Boolean
isoL b = if b then true else false
isoR :: Boolean -> Bool
isoR b = test b True False
Lists
Now let’s talk about lists. One of the interesting things is lists are the first recursive data type we’ve dealt with so far.
Defining the API for lists isn’t entirely clear either. We want a small set of functions that can easily cover any conceivable operations for a list.
The simplest way to do this is to realize that we can do exactly 3 things with lists.
- Make an empty list
- Add a new element to the front of an existing list
- Pattern match on them
We can represent this with 3 functions
type List a = ...
nil :: List a
cons :: a -> List a -> List a
match :: List a -> b -> (a -> List a -> b) -> b
If match
looks confusing just remember that
Is really the same as
In this way match
is just the pure functional version of pattern matching. We can actually simplify the API by realizing that rather than this awkward match
construct, we can use something cleaner.
foldr
forms a much more pleasant API to work with since it’s really the most primitive form of “recursing” on a list.
match :: List a -> (a -> List a -> b) -> b -> b
match list f b = fst $ foldr list worker (b, nil)
where worker x (b, xs) = (f x xs, cons x xs)
The especially nice thing about foldr
is that it doesn’t mention List a
in its two “destruction” functions, all the recursion is handled in the implementation.
We can implement CR lists trivially using foldr
type List a = forall b. (a -> b -> b) -> b -> b
nil = \ _ nil -> nil
cons x xs = \ cons nil -> x `cons` xs cons nil
foldr list cons nil = list cons nil
Notice that we handle the recursion in the list type by having a b
as an argument? This is similar to how the accumulator to foldr
gets the processed tail of the list. This is a common technique for handling recursion in our church representations.
Last but not least, the isomorphism arises from foldr (:) []
,
Either
The last case that we’ll look at is Either
. Like Pair
, Either
has 3 different operations.
This is pretty easy to implement with Either
Once again, the trick to encoding this as a function falls right out of the API. In this case we use the type of or
Last but not least, let’s quickly rattle off our isomorphism.
The Pattern
So now we can talk about the underlying pattern in CRs. First remember that for any type T
, we have a list of n
distinct constructors T1
T2
T3
…Tn
. Each of the constructors has a m
fields T11
, T12
, T13
…
Now the church representation of such a type T
is
forall c. (T11 -> T12 -> T13 -> .. -> c)
-> (T21 -> T22 -> T23 -> .. -> c)
...
-> (Tn1 -> Tn2 -> Tn3 -> .. -> c)
-> c
This pattern doesn’t map quite as nicely to recursive types. Here we have to take the extra step of substituting all occurrences of T
for c
in our resulting church representation.
This is actually such a pleasant pattern to work with that I’ve written a library for automatically reifying a type between its church representation and concrete form.
Wrap Up
Hopefully you now understand what a church representation is. It’s worth noting that a lot of stuff Haskellers stumble upon daily are really church representations in disguise.
My favorite example is maybe
, this function takes a success and failure continuation with a Maybe
and produces a value. With a little bit of imagination, one can realize that this is really just a function mapping a Maybe
to a church representation!
If you’re thinking that CRs are pretty cool! Now might be a time to take a look at one of my previous posts on deriving them automagically.
comments powered by Disqus