homolo.gy

Church encoding

July 7, 2014

It’s possible to represent data types purely as functions. For example, consider the following type:

data What = Foo | Bar | Baz

Ignoring undefined, What has three inhabitants given by the three nullary value constructors. This can be represented via the Church encoding as follows:

type What1 = forall b. b -> b -> b -> b

What1 also has 3 inhabitants, due to parametricity of b: a function of type What1 must evaluate to one of its three arguments. The mapping between these two types is given by the following two functions:

to :: What -> What1
to Foo = \x y z -> x
to Bar = \x y z -> y
to Baz = \x y z -> z

from :: What1 -> What
from f = f Foo Bar Baz

Note that to extract a value of type What back out, all you have to do is pass in the value constructors.

What happens when the data constructor is not nullary?

data Where = Location String | Nowhere

It seems plausible to take all the instances of the type in the signature and just replace them with b. Then, to construct the encoded version, we use String -> b for Location and b for Nowhere. Thus:

type Where1 = forall b. (String -> b) -> b -> b

to :: Where -> Where1
to (Location s) = \f x -> f s
to Nowhere      = \f x -> x

from :: Where1 -> Where
from f = f Location Nowhere

What about a unary type constructor? Modulo newtypes, everything is more of the same:

type Maybe1 a = forall b. (a -> b) -> b -> b

to :: Maybe a -> Maybe1 a
to (Just s) = \f x -> f s
to Nothing  = \f x -> x

from :: Maybe1 a -> Maybe a
from f = f Just Nothing

The situation becomes a little more illuminating when recursive types are considered. Consider the following encoded version of the list type:

type List1 a = forall b. (a -> b -> b) -> b -> b

But this is just a fold on a specific list! It turns out that the encoding of any data type corresponds to its fold.

Exercise: What do to and from look like for List1 a?