Well, we can comment the Churchlist type this way to clarify it:
-- Tell me...
type Churchlist t u = (t -> u -> u) -- ...how to handle a pair
-> u -- ...and how to handle an empty list
-> u -- ...and then I'll transform a list into
-- the type you want
Note that this is intimately related to the foldr
function:
foldr :: (t -> u -> u) -> u -> [t] -> u
foldr k z [] = z
foldr k z (x:xs) = k x (foldr k z xs)
foldr
is a very general function that can implement all sorts of other list functions. A trivial example that will help you is implementing a list copy with foldr
:
copyList :: [t] -> [t]
copyList xs = foldr (:) [] xs
Using the commented type above, foldr (:) []
means this: "if you see an empty list return the empty list, and if you see a pair return head:tailResult
."
Using Churchlist
, you can easily write the counterpart this way:
-- Note that the definitions of nil and cons mirror the two foldr equations!
nil :: Churchlist t u
nil = \k z -> z
cons :: t -> Churchlist t u -> Churchlist t u
cons x xs = \k z -> k x (xs k z)
copyChurchlist :: ChurchList t u -> Churchlist t u
copyChurchlist xs = xs cons nil
Now, to implement map
, you just need to replace cons
with a suitable function, like this:
map :: (a -> b) -> [a] -> [b]
map f xs = foldr (\x xs' -> f x:xs') [] xs
Mapping is like copying a list, except that instead of just copying the elements verbatim you apply f
to each of them.
Study all of this carefully, and you should be able to write mapChurchlist :: (t -> t') -> Churchlist t u -> Churchlist t' u
on your own.
Extra exercise (easy): write these list functions in terms of foldr
, and write counterparts for Churchlist
:
filter :: (a -> Bool) -> [a] -> [a]
append :: [a] -> [a] -> [a]
-- Return first element of list that satisfies predicate, or Nothing
find :: (a -> Bool) -> [a] -> Maybe a
If you're feeling like tackling something harder, try writing tail
for Churchlist
. (Start by writing tail
for [a]
using foldr
.)