by Tom Ellis on 4th November 2012
foldr made of?This is a common recursion pattern:
func1 [] = []
func1 (x:xs) = f x : func1 xs
It is so common that there is a version of it parameterised in f. It is called map:
map :: (a -> b) -> [a] -> [b]
map f [] = []
map f (x:xs) = f x : map f xs
Here is another common recursion pattern:
func2 [] = z
func2 (x:xs) = g x (func2 xs)
Again there’s a parameterised version, this time in g and z. It is called foldr:
foldr :: (a -> b -> b) -> b -> [a] -> b
foldr g z [] = z
foldr g z (x:xs) = g x (foldr g z xs)
Using foldr we can implement map:
map' :: (a -> b) -> [a] -> [b]
map' f = foldr ((:) . f) []
for then
map' f [] == foldr ((:) . f) [] [] == []
map' f (x:xs) == foldr ((:) . f) [] (x:xs) == ((:) . f) x (foldr ((:) . f) xs
== f x : (foldr ((:) . f) xs = f x : map' f xs
But using map alone we cannot implement foldr. (This statement is morally true, but I’m not sure of the formal sense in which it is true). Is there something less powerful than foldr that we can combine with map to recover foldr? Surprisingly the answer is “yes”! Define
compose :: [a -> a] -> a -> a
compose [] = id
compose (f:fs) = f . compose fs
Then write
foldr' :: (a -> b -> b) -> b -> [a] -> b
foldr' g z xs = compose (map g xs) z
and we have
foldr' g z [] == compose [] z == id z == z
foldr' g z (x:xs) == compose (map g (x:xs)) z
== compose (g x : map g xs) z
== (g x . compose (map g xs)) z
== g x (compose (map g xs) z)
== g x (foldr' g z xs)
And from foldr we can recover compose:
compose' fs = foldr (.) id fs
Then
compose' [] == foldr (.) id [] == id
compose' (f:fs) == foldr (.) id (f:fs) == (.) f (foldr (.) id fs)
== f . (foldr (.) id fs) = f . compose' fs
foldr made of?foldr is made of map, plus the ability to compose a chain of endomorphisms. For some reason I find this fascinating!