In Real World Haskell, Chapter 4. on Functional Programming:
Write foldl with foldr:
-- file: ch04/Fold.hs
myFoldl :: (a -> b -> a) -> a -> [b] -> a
myFoldl f z xs = foldr step id xs z
where step x g a = g (f a x)
The above code confused me a lot, and somebody called dps rewrote it with a meaningful name to make it a bit clearer:
myFoldl stepL zeroL xs = (foldr stepR id xs) zeroL
where stepR lastL accR accInitL = accR (stepL accInitL lastL)
Somebody else, Jef G, then did an excellent job by providing an example and showing the underlying mechanism step by step:
myFoldl (+) 0 [1, 2, 3]
= (foldR step id [1, 2, 3]) 0
= (step 1 (step 2 (step 3 id))) 0
= (step 1 (step 2 (\a3 -> id ((+) a3 3)))) 0
= (step 1 (\a2 -> (\a3 -> id ((+) a3 3)) ((+) a2 2))) 0
= (\a1 -> (\a2 -> (\a3 -> id ((+) a3 3)) ((+) a2 2)) ((+) a1 1)) 0
= (\a1 -> (\a2 -> (\a3 -> (+) a3 3) ((+) a2 2)) ((+) a1 1)) 0
= (\a1 -> (\a2 -> (+) ((+) a2 2) 3) ((+) a1 1)) 0
= (\a1 -> (+) ((+) ((+) a1 1) 2) 3) 0
= (+) ((+) ((+) 0 1) 2) 3
= ((0 + 1) + 2) + 3
But I still cannot fully understand that, here are my questions:
- What is the id function for? What is the role of? Why should we need it here?
- In the above example, id function is the accumulator in the lambda function?
- foldr’s prototype is
foldr :: (a -> b -> b) -> b -> [a] -> b, and the first parameter is a function which need two parameters, but the step function in the myFoldl’s implementation uses 3 parameters, I’m complelely confused!
Some explanations are in order!
What is the id function for? What is the role of? Why should we need it here?
idis the identity function,id x = x, and is used as the equivalent of zero when building up a chain of functions with function composition,(.). You can find it defined in the Prelude.In the above example, id function is the accumulator in the lambda function?
The accumulator is a function that is being built up via repeated function application. There’s no explicit lambda, since we name the accumulator,
step. You can write it with a lambda if you want:Or as Graham Hutton would write:
foldr’s prototype is foldr :: (a -> b -> b) -> b -> [a] -> b
A Haskell programmer would say that the type of
foldris(a -> b -> b) -> b -> [a] -> b.and the first parameter is a function which need two parameters, but the step function in the myFoldl’s implementation uses 3 parameters, I’m complelely confused
This is confusing and magical! We play a trick and replace the accumulator with a function, which is in turn applied to the initial value to yield a result.
Graham Hutton explains the trick to turn
foldlintofoldrin the above article. We start by writing down a recursive definition offoldl:And then refactor it via the static argument transformation on
f:Let’s now rewrite
gso as to float thevinwards:Which is the same as thinking of
gas a function of one argument, that returns a function:Now we have
g, a function that recursively walks a list, apply some functionf. The final value is the identity function, and each step results in a function as well.But, we have handy already a very similar recursive function on lists,
foldr!This looks like a very similar recursive scheme to our
gfunction. Now the trick: using all the available magic at hand (aka Bird, Meertens and Malcolm) we apply a special rule, the universal property of fold, which is an equivalence between two definitions for a functiongthat processes lists, stated as:So, the universal property of folds states that:
where
gmust be equivalent to the two equations, for somekandv:From our earlier foldl designs, we know
v == id. For the second equation though, we needto calculate the definition of
k:Which, substituting our calculated definitions of
kandvyields adefinition of foldl as:
The recursive
gis replaced with the foldr combinator, and the accumulator becomes a function built via a chain of compositions offat each element of the list, in reverse order (so we fold left instead of right).This is definitely somewhat advanced, so to deeply understand this transformation, the universal property of folds, that makes the transformation possible, I recommend Hutton’s tutorial, linked below.
References