Trying this in ghci:
foldl (:) [1] [2,3,4]
which gives the following error:
<interactive>:51:7: error:
• Couldn't match type ‘a’ with ‘[a]’
Expected: [a] -> [[a]] -> [a]
Actual: [a] -> [[a]] -> [[a]]
‘a’ is a rigid type variable bound by
the inferred type of it :: [a]
at <interactive>:51:1-21
• In the first argument of ‘foldl’, namely ‘(:)’
In the expression: foldl (:) [1] [2, 3, 4]
In an equation for ‘it’: it = foldl (:) [1] [2, 3, 4]
• Relevant bindings include it :: [a] (bound at <interactive>:51:1)
But the same thing works with foldr
foldr (:) [1] [2,3,4]
which outputs [2,3,4,1]
Shouldn't the foldl output (above) be [1,2,3,4]? As per my understanding, the only difference between foldl and foldr is where the accumulator is placed so I am not understanding this error.
The signatures of foldl and foldr` differ. Indeed:
foldl :: Foldable f => (b -> a -> b) -> b -> t a -> b
foldr :: Foldable f => (a -> b -> b) -> b -> t a -> b
Notice the different order of a and b in the "fold function" (first parameter).
This is likely to stress that foldl f z [x1, x2, …, xn] is defined as:
foldl f z [x1, x2, …, xn] = f (… (f (f z x1) x2) …) xn
whereas for foldr f z [x1, x2, …, xn], we get:
foldr f z [x1, x2, …, xn] = f x1 (f x2 (… (f xn z) …))
You thus can work with:
foldl (flip (:)) [1] [2,3,4]
But this will not give the same result, since we construct:
foldl (flip (:)) [1] [2,3,4] = 4 : (3 : (2 : [1]))
= [4, 3, 2, 1]
I have seen that it is possible to define the append function in Haskell in this way
append :: [a] -> [a] -> [a]
append = flip (foldr (:))
I am looking for an explanation of how this works. My main problem is I don't know how foldr gets the lists as arguments. A full explanation of the entire implementation would be good though.
foldr needs three arguments: an accumulator function, an initial accumulator value, and a list (well, a Foldable).
foldr (:), therefore, requires two more arguments: initial accumulator, and list.
What is being done here, is cleverly using one of the two lists as the initial accumulator.
Then, for each element in the other list, it is cons'ed to the initial list; which results in all the elements in the other list being appended to the initial list.
Basically, append [1,2,3] [4,5,6] here is equivalent to foldr (:) [4,5,6] [1,2,3], which ends up doing (1:(2:(3:[4,5,6]))), resulting in [1,2,3,4,5,6]
flip :: (a -> b -> c) -> b -> a -> c is implemented as:
flip :: (a -> b -> c) -> b -> a -> c
flip f x y = f y x
So it is given a function f and two parameters x and y, and it applies these arguments like f y x. So that means that the append = flip (foldr (:)) is short for:
append :: [a] -> [a] -> [a]
append xs ys = foldr (:) ys xs
Now you can see foldr as a catamorphsim [wiki] for a list. Indeed, for foldr f z ls, it replaces the cons (:) in the list of ls with f, and the empty list [] with z. For lists foldr is implemented as:
foldr :: (a -> b -> b) -> b -> [a] -> b
foldr k z = go
where go [] = z
go (y:ys) = y `k` go ys
So that means for foldr (+) 0 [x1, x2, x3] which is equal to foldr (+) 0 (x1 : (x2 : (x3 : []))), it is equal to x1 + (x2 + (x3 + 0)) or less verbose x1 + x2 + x3 + 0.
So here for foldr (:) ys, with a list [x1, x2, x3], this is equal to (x1 : (x2 : (x3 : ys))), which is thus equal to a list of [x1, x2, x3, y1, y2, …, yn].
flip f xs ys = f ys xs, so then
append xs ys = flip (foldr (:)) xs ys
= foldr (:) ys xs
Now, foldr g z xs replaces every : in xs with g, and it replaces the final [] in xs with z, so we get
append (x1 : x2 : ... : xn : []) ys = foldr (:) ys xs
-- replace `:` with `:` and `[]` with `ys`:
= (x1 : x2 : ... : xn : ys)
As to where it gets the lists from, they are simply left implicit. Haskell allows to shorten a definition by removing the same last parameter from its left hand side, and its right hand side, if it's not needed in what remains:
foo x y z = g x x y z
foo x y = g x x y
foo x = g x x
= join g x
foo = join g
x can't be simply removed, as it is needed in the remaining g x. But after transforming it with the use of join (details of what it is and why it works don't matter here) it becomes possible to remove the remaining x as well.
In your case the arguments are used just once, and their order is flipped by using the flip function:
append xs ys = foldr (:) ys xs
= flip (foldr (:)) xs ys
append xs = flip (foldr (:)) xs
append = flip (foldr (:))
I'm having trouble comprehending the definition:
ifoldr :: Foldable f => (Int -> a -> b -> b) -> b -> f a -> b
ifoldr f z xs = foldr (\ x g i -> i `seq` f i x (g (i+1))) (const z) xs 0
In particular, it seems to avoid space leaks by avoiding zip [1..] and at the same time it seems to derive a new fold "step function" that is given additional argument to front, but this argument is passed last in \ x g i!
Is this equivalent to f' x (foldr f' z xs) for some definition f' = _unknown_ f with retaining non-strictness properties?
In short: the foldr produces a function (not a list of values), and that function will then generate that list.
Let us first ignore the foldr for a while, and concentrate on the function used in the foldr, let us call this function eval:
eval x g i = seq i (f i x (g (i+1))))
We will ignore the seq here: yes it has some semantics: evaluating (to weak head normal form) the i and checking if i is bottom, but let us assume that this will not introduce a bottom. So eval is - more or less - equivalent to:
eval x g i = f i x (g (i+1))
Now we can take the foldr context back into account:
ifoldr f = foldr eval (const z) xs 0
where eval x g i = f i x (g (i+1))
Now foldr is defined (for lists, but let us keep things simple here), as:
foldr _ z [] = z
foldr f z (x:xs) = f x (foldr f z xs)
For a list with three elements [x1, x2, x3], this thus means that:
foldr eval (const z) [x1, x2, x3]
looks like:
-- foldr eval (const z) [x1, x2, x3] is equivalent to
eval x1 (eval x2 (eval x3 (const z)))
Since eval is defined as above, that means that we can specialize it to:
\i1 -> f i1 x1 ((\i2 -> f i2 x2 (\i3 -> f i3 x3 (const z)) (i2 + 1)) (i1 + 1))
Or perhaps in a way that makes the structure more clear:
\i1 -> (
f i1 x1
\i2 -> (
f i2 x2
\i3 -> (
f i3 x3
(const z) (i3+1)
) (i2+1)
) (i1+1)
)
So as you can see the outer function takes a parameter (here i1), and makes a call to f with i1 (the index), x1 (the first item), and as last item the result of a call that is the "fold" of the remaining list. We thus make a call with i2 as parameter, but that i2 is bound with i1+1.
So if we perform substitution (substuting i3 with i2 + 1), which is how lambda calculus works, we obtain:
\i1 -> (
f i1 x1
\i2 -> (
f i2 x2
(
f (i2+1) x3
(const z) (i2+1+1)
)
) (i1+1)
)
and furthermore we can substitute i2 with i1+1:
\i1 -> (
f i1 x1
(
f (i1+1) x2
(
f (i2+1) x3
(const z) (i1+1+1+1)
)
)
Since (const z) maps to z, regardless what the parameter is, we can substitute (const z) (i1+1+1+1) with z, so:
\i1 -> (
f i1 x1
(
f (i1+1) x2
(
f (i1+1+1) x3
z
)
)
So now we know what foldr eval (const z) [x1, x2, x3] maps to, but there is a final fuction application: the 0 at the end.
So that means that we make a call to the above defined lambda-expression with 0, so this collapses to:
\i1 -> (
f i1 x1
(
f (i1+1) x2
(
f (i1+1+1) x3
z
)
) 0
and thus:
(
f 0 x1
(
f (0+1) x2
(
f (0+1+1) x3
z
)
)
or in a compact form:
(f 0 x1 (f 1 x2 (f 2 x3 z)))
So we managed to inject indices in our solution.
Now the seq of course has a function: it will prevent making huge (left-recursive) expression trees for the index, instead of ((((1+1)+1)+1)+1)+1, it will ensure that each time we increment it, it is immediately evaluated, so that we will never obtain 1+1+1, but always 2+1, and immidiately resolve it to 3.
If (as indeed it is)
foldr c n (x:xs) = c x (foldr c n xs) :: t
c x r = ... -- r: mnemonic: recursive result
c x r :: t , r :: t , n :: t -- same t
then surely (by eta-expansion)
foldr c n (x:xs) i = c x (foldr c n xs) i :: t
c x r i = ... -- c = (\ x r i -> ... )
c x r i :: t , r i :: t , n i :: t -- same t
and so we can have
ifoldr f n (x:xs) = foldr c n (x:xs) i = c x (foldr c n xs) i :: t
= f i x (foldr c n xs i') :: t
c x r i = f i x (r i')
c x r i :: t , r i :: t , n i :: t , f i x :: t -> t
and that's exactly what you got there.
Given the following example
foldr(\ x y -> ........
if the input is a list for example [1,2,3]
what is x and what is y?
Let's take a look at the type for foldr.
foldr :: (a -> b -> b) -> b -> [a] -> b
Since you're providing the function that uses x and y, you can see from the type that x will be a value from your list ([1,2,3]), and y must be the accumulator value, which you initialize with the second parameter to foldr.
The definition of foldr is
foldr f z [] = z
foldr f z (x:xs) = f x (foldr f z xs)
You can just apply it directly in your example:
foldr (\x y -> foo x y) z [1,2,3]
=
(\x y -> foo x y) 1 (foldr (\x y -> foo x y) z [2,3])
=
foo 1 (foldr (\x y -> foo x y) z [2,3])
So x is 1 and y is foldr (\x y ...) z [2,3]).
In general you can think of foldr f z as replacing every (:) in a list with f, and the [] with z. So foldr f z [a,b,c,d] = f a (f b (f c (f d z))) (since [a,b,c,d] = (:) a ((:) b ((:) c ((:) d []))).
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?
id is 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:
foldl f a bs = foldr (\b g x -> g (f x b)) id bs a
Or as Graham Hutton would write:
5.1 The foldl operator
Now let us generalise from the suml example and consider the standard operator foldl that processes the elements of a list in left-to-right order by using a function f to combine values, and a value v as the starting value:
foldl :: (β → α → β) → β → ([α] → β)
foldl f v [ ] = v
foldl f v (x : xs) = foldl f (f v x) xs
Using this operator, suml can be redefined simply by suml = foldl (+) 0. Many other functions can be defined in a simple way using foldl. For example, the standard function reverse can redefined using foldl as follows:
reverse :: [α] → [α]
reverse = foldl (λxs x → x : xs) [ ]
This definition is more efficient than our original definition using fold, because it avoids the use of the inefficient append operator (++) for lists.
A simple generalisation of the calculation in the previous section for the function suml shows how to redefine the function foldl in terms of fold:
foldl f v xs = fold (λx g → (λa → g (f a x))) id xs v
In contrast, it is not possible to redefine fold in terms of foldl, due to the fact that
foldl is strict in the tail of its list argument but fold is not. There are a number of useful ‘duality theorems’ concerning fold and foldl, and also some guidelines for deciding which operator is best suited to particular applications (Bird, 1998).
foldr's prototype is foldr :: (a -> b -> b) -> b -> [a] -> b
A Haskell programmer would say that the type of foldr is (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 foldl into foldr in the above article. We start by writing down a recursive definition of foldl:
foldl :: (a -> b -> a) -> a -> [b] -> a
foldl f v [] = v
foldl f v (x : xs) = foldl f (f v x) xs
And then refactor it via the static argument transformation on f:
foldl :: (a -> b -> a) -> a -> [b] -> a
foldl f v xs = g xs v
where
g [] v = v
g (x:xs) v = g xs (f v x)
Let's now rewrite g so as to float the v inwards:
foldl f v xs = g xs v
where
g [] = \v -> v
g (x:xs) = \v -> g xs (f v x)
Which is the same as thinking of g as a function of one argument, that returns a function:
foldl f v xs = g xs v
where
g [] = id
g (x:xs) = \v -> g xs (f v x)
Now we have g, a function that recursively walks a list, apply some function f. 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!
2 The fold operator
The fold operator has its origins in recursion theory (Kleene, 1952), while the use
of fold as a central concept in a programming language dates back to the reduction operator of APL (Iverson, 1962), and later to the insertion operator of FP (Backus,
1978). In Haskell, the fold operator for lists can be defined as follows:
fold :: (α → β → β) → β → ([α] → β)
fold f v [ ] = v
fold f v (x : xs) = f x (fold f v xs)
That is, given a function f of type α → β → β and a value v of type β, the function
fold f v processes a list of type [α] to give a value of type β by replacing the nil
constructor [] at the end of the list by the value v, and each cons constructor (:) within the list by the function f. In this manner, the fold operator encapsulates a simple pattern of recursion for processing lists, in which the two constructors for lists are simply replaced by other values and functions. A number of familiar functions on lists have a simple definition using fold.
This looks like a very similar recursive scheme to our g function. 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 function g that processes lists, stated as:
g [] = v
g (x:xs) = f x (g xs)
if and only if
g = fold f v
So, the universal property of folds states that:
g = foldr k v
where g must be equivalent to the two equations, for some k and v:
g [] = v
g (x:xs) = k x (g xs)
From our earlier foldl designs, we know v == id. For the second equation though, we need
to calculate the definition of k:
g (x:xs) = k x (g xs)
<=> g (x:xs) v = k x (g xs) v -- accumulator of functions
<=> g xs (f v x) = k x (g xs) v -- definition of foldl
<= g' (f v x) = k x g' v -- generalize (g xs) to g'
<=> k = \x g' -> (\a -> g' (f v x)) -- expand k. recursion captured in g'
Which, substituting our calculated definitions of k and v yields a
definition of foldl as:
foldl :: (a -> b -> a) -> a -> [b] -> a
foldl f v xs =
foldr
(\x g -> (\a -> g (f v x)))
id
xs
v
The recursive g is replaced with the foldr combinator, and the accumulator becomes a function built via a chain of compositions of f at 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
Haskell Wiki: Foldl as foldr
A tutorial on the universality and expressiveness of fold, Graham Hutton, J. Functional Programming 9 (4): 355–372, July 1999.
Malcolm, G. Algebraic data types and program transformation., PhD thesis, Groningen University.
Consider the type of foldr:
foldr :: (b -> a -> a) -> a -> [b] -> a
Whereas the type of step is something like b -> (a -> a) -> a -> a. Since step is getting passed to foldr, we can conclude that in this case the fold has a type like (b -> (a -> a) -> (a -> a)) -> (a -> a) -> [b] -> (a -> a).
Don't be confused by the different meanings of a in different signatures; it's just a type variable. Also, keep in mind that the function arrow is right associative, so a -> b -> c is the same thing as a -> (b -> c).
So, yes, the accumulator value for the foldr is a function of type a -> a, and the initial value is id. This makes some sense, because id is a function that doesn't do anything--it's the same reason you'd start with zero as the initial value when adding all the values in a list.
As for step taking three arguments, try rewriting it like this:
step :: b -> (a -> a) -> (a -> a)
step x g = \a -> g (f a x)
Does that make it easier to see what's going on? It takes an extra parameter because it's returning a function, and the two ways of writing it are equivalent. Note also the extra parameter after the foldr: (foldr step id xs) z. The part in parentheses is the fold itself, which returns a function, which is then applied to z.
(quickly skim through my answers [1], [2], [3], [4] to make sure you understand Haskell's syntax, higher-order functions, currying, function composition, $ operator, infix/prefix operators, sections and lambdas)
Universal property of fold
A fold is just a codification of certain kinds of recursion. And universality property simply states that, if your recursion conforms to a certain form, it can be transformed into fold according to some formal rules. And conversely, every fold can be transformed into a recursion of that kind. Once again, some recursions can be translated into folds that give exactly the same answer, and some recursions can't, and there is an exact procedure to do that.
Basically, if your recursive function works on lists an looks like on the left, you can transform it to fold one the right, substituting f and v for what actually is there.
g [] = v ⇒
g (x:xs) = f x (g xs) ⇒ g = foldr f v
For example:
sum [] = 0 {- recursion becomes fold -}
sum (x:xs) = x + sum xs ⇒ sum = foldr 0 (+)
Here v = 0 and sum (x:xs) = x + sum xs is equivalent to sum (x:xs) = (+) x (sum xs), therefore f = (+). 2 more examples
product [] = 1
product (x:xs) = x * product xs ⇒ product = foldr 1 (*)
length [] = 0
length (x:xs) = 1 + length xs ⇒ length = foldr (\_ a -> 1 + a) 0
Exercise:
Implement map, filter, reverse, concat and concatMap recursively, just like the above functions on the left side.
Convert these 5 functions to foldr according to a formula above, that is, substituting f and v in the fold formula on the right.
Foldl via foldr
How to write a recursive function that sums numbers up from left to right?
sum [] = 0 -- given `sum [1,2,3]` expands into `(1 + (2 + 3))`
sum (x:xs) = x + sum xs
The first recursive function that comes to find fully expands before even starts adding up, that's not what we need. One approach is to create a recursive function that has accumulator, that immediately adds up numbers on each step (read about tail recursion to learn more about recursion strategies):
suml :: [a] -> a
suml xs = suml' xs 0
where suml' [] n = n -- auxiliary function
suml' (x:xs) n = suml' xs (n+x)
Alright, stop! Run this code in GHCi and make you sure you understand how it works, then carefully and thoughtfully proceed. suml can't be redefined with a fold, but suml' can be.
suml' [] = v -- equivalent: v n = n
suml' (x:xs) n = f x (suml' xs) n
suml' [] n = n from function definition, right? And v = suml' [] from the universal property formula. Together this gives v n = n, a function that immediately returns whatever it receives: v = id. Let's calculate f:
suml' (x:xs) n = f x (suml' xs) n
-- expand suml' definition
suml' xs (n+x) = f x (suml' xs) n
-- replace `suml' xs` with `g`
g (n+x) = f x g n
Thus, suml' = foldr (\x g n -> g (n+x)) id and, thus, suml = foldr (\x g n -> g (n+x)) id xs 0.
foldr (\x g n -> g (n + x)) id [1..10] 0 -- return 55
Now we just need to generalize, replace + by a variable function:
foldl f a xs = foldr (\x g n -> g (n `f` x)) id xs a
foldl (-) 10 [1..5] -- returns -5
Conclusion
Now read Graham Hutton's A tutorial on the universality and expressiveness of fold. Get some pen and paper, try to figure everything that he writes until you get derive most of the folds by yourself. Don't sweat if you don't understand something, you can always return later, but don't procrastinate much either.
Here's my proof that foldl can be expressed in terms of foldr, which I find pretty simple apart from the name spaghetti the step function introduces.
The proposition is that foldl f z xs is equivalent to
myfoldl f z xs = foldr step_f id xs z
where step_f x g a = g (f a x)
The first important thing to notice here is that the right hand side of the first line is actually evaluated as
(foldr step_f id xs) z
since foldr only takes three parameters. This already hints that the foldr will calculate not a value but a curried function, which is then applied to z. There are two cases to investigate to find out whether myfoldl is foldl:
Base case: empty list
myfoldl f z []
= foldr step_f id [] z (by definition of myfoldl)
= id z (by definition of foldr)
= z
foldl f z []
= z (by definition of foldl)
Non-empty list
myfoldl f z (x:xs)
= foldr step_f id (x:xs) z (by definition of myfoldl)
= step_f x (foldr step_f id xs) z (-> apply step_f)
= (foldr step_f id xs) (f z x) (-> remove parentheses)
= foldr step_f id xs (f z x)
= myfoldl f (f z x) xs (definition of myfoldl)
foldl f z (x:xs)
= foldl f (f z x) xs
Since in 2. the first and the last line have the same form in both cases, it can be used to fold the list down until xs == [], in which case 1. guarantees the same result. So by induction, myfoldl == foldl.
There is no Royal Road to Mathematics, nor even through Haskell. Let
h z = (foldr step id xs) z where
step x g = \a -> g (f a x)
What the heck is h z? Assume that xs = [x0, x1, x2].
Apply the definition of foldr:
h z = (step x0 (step x1 (step x2 id))) z
Apply the definition of step:
= (\a0 -> (\a1 -> (\a2 -> id (f a2 x2)) (f a1 x1)) (f a0 x0)) z
Substitute into the lambda functions:
= (\a1 -> (\a2 -> id (f a2 x2)) (f a1 x1)) (f z x0)
= (\a2 -> id (f a2 x2)) (f (f z x0) x1)
= id (f (f (f z x0) x1) x2)
Apply definition of id :
= f (f (f z x0) x1) x2
Apply definition of foldl :
= foldl f z [x0, x1, x2]
Is it a Royal Road or what?
I'm posting the answer for those people who might find this approach better suited to their way of thinking. The answer possibly contains redundant information and thoughts, but it is what I needed in order to tackle the problem. Furthermore, since this is yet another answer to the same question, it's obvious that it has substantial overlaps with the other answers, however it tells the tale of how I could grasp this concept.
Indeed I started to write down this notes as a personal record of my thoughts while trying to understand this topic. It took all the day for me to touch the core of it, if I really have got it.
My long way to understanding this simple exercise
Easy part: what do we need to determine?
What happens with the following example call
foldl f z [1,2,3,4]
can be visualized with the following diagram (which is on Wikipedia, but I first saw it on another answer):
_____results in a number
/
f f (f (f (f z 1) 2) 3) 4
/ \
f 4 f (f (f z 1) 2) 3
/ \
f 3 f (f z 1) 2
/ \
f 2 f z 1
/ \
z 1
(As a side note, when using foldl each applications of f is not performed, and the expressions are thunked just the way I wrote them above; in principle, they could be computed as you go bottom-top, and that's exactly what foldl' does.)
The exercise essentially challenges us to use foldr instead of foldl by appropriately changing the step function (so we use s instead of f) and the initial accumulator (so we use ? instead of z); the list stays the same, otherwise what are we talking about?
The call to foldr has to look like this:
foldr s ? [1,2,3,4]
and the corresponding diagram is this:
_____what does the last call return?
/
s
/ \
1 s
/ \
2 s
/ \
3 s
/ \
4 ? <--- what is the initial accumulator?
The call results in
s 1 (s 2 (s 3 (s 4 ?)))
What are s and ?? And what are their types? It looks like s it's a two argument function, much like f, but let's not jump to conclusions. Also, let's leave ? aside for a moment, and let's observe that z has to come into play as soon as 1 comes into play; however, how can z come into play in the call to the maybe-two-argument s function, namely in the call s 1 (…)? We can solve this part of the enigma by choosing an s which takes 3 arguments, rather than the 2 we mentioned earlier, so that the outermost call s 1 (…) will result in a function taking one argument, which we can pass z to!
This means that we want the original call, which expands to
f (f (f (f z 1) 2) 3) 4
to be equivalent to
s 1 (s 2 (s 3 (s 4 ?))) z
or, in other words, we want the partially applied function
s 1 (s 2 (s 3 (s 4 ?)))
to be equivalent to the following lambda function
(\z -> f (f (f (f z 1) 2) 3) 4)
Again, the "only" pieces we need are s and ?.
Turning point: recognize function composition
Let's redraw the previous diagram and write on the right what we want each call to s be equivalent to:
s s 1 (…) == (\z -> f (f (f (f z 1) 2) 3) 4)
/ \
1 s s 2 (…) == (\z -> f (f (f z 2) 3) 4)
/ \
2 s s 3 (…) == (\z -> f (f z 3) 4)
/ \
3 s s 4 ? == (\z -> f z 4)
/ \
4 ? <--- what is the initial accumulator?
I hope it's clear from the structure of the diagram that the (…) on each line is the right hand side of the line below it; better, it is the function returned from the previous (below) call to s.
It should be also clear that a call to s with arguments x and y is the (full) application of y to the partial application of f to the only argument x (as its second argument). Since the partial application of f to x can be written as the lambda (\z -> f z x), fully applying y to it results in the lambda (\z -> y (f z x)), which in this case I would rewrite as y . (\z -> f z x); translating the words into an expression for s we get
s x y = y . (\z -> f z x)
(This is the same as s x y z = y (f z x), which is the same as the book, if you rename the variables.)
The last bit is: what is the initial "value" ? of the accumulator? The above diagram can be rewritten by expanding the nested calls to make them composition chains:
s s 1 (…) == (\z -> f z 4) . (\z -> f z 3) . (\z -> f z 2) . (\z -> f z 1)
/ \
1 s s 2 (…) == (\z -> f z 4) . (\z -> f z 3) . (\z -> f z 2)
/ \
2 s s 3 (…) == (\z -> f z 4) . (\z -> f z 3)
/ \
3 s s 4 ? == (\z -> f z 4)
/ \
4 ? <--- what is the initial accumulator?
We here see that s simply "piles up" successive partial applications of f, but the y in s x y = y . (\z -> f z x) suggests that the interpretation of s 4 ? (and, in turn, all the others) misses a leading function to be composed with the leftmost lambda.
That's just our ? function: it's time to give it a reason for its existence, beside occupying a place in the call to foldr. What can we choose it to be, in order not to change the resulting functions? Answer: id, the identity function, which is also the identity element with respect to the composition operator (.).
s s 1 (…) == id . (\z -> f z 4) . (\z -> f z 3) . (\z -> f z 2) . (\z -> f z 1)
/ \
1 s s 2 (…) == id . (\z -> f z 4) . (\z -> f z 3) . (\z -> f z 2)
/ \
2 s s 3 (…) == id . (\z -> f z 4) . (\z -> f z 3)
/ \
3 s s 4 id == id . (\z -> f z 4)
/ \
4 id
So the sought function is
myFoldl f z xs = foldr (\x g a -> g (f a x)) id xs z
foldr step zero (x:xs) = step x (foldr step zero xs)
foldr _ zero [] = zero
myFold f z xs = foldr step id xs z
where step x g a = g (f a x)
myFold (+) 0 [1, 2, 3] =
foldr step id [1, 2, 3] 0
-- Expanding foldr function
step 1 (foldr step id [2, 3]) 0
step 1 (step 2 (foldr step id [3])) 0
step 1 (step 2 (step 3 (foldr step id []))) 0
-- Expanding step function if it is possible
step 1 (step 2 (step 3 id)) 0
step 2 (step 3 id) (0 + 1)
step 3 id ((0 + 1) + 2)
id (((0 + 1) + 2) + 3)
Well, at least, this helped me. Even it is not quite right.
This might help, I tried expanding in a different way.
myFoldl (+) 0 [1,2,3] =
foldr step id [1,2,3] 0 =
foldr step (\a -> id (a+3)) [1,2] 0 =
foldr step (\b -> (\a -> id (a+3)) (b+2)) [1] 0 =
foldr step (\b -> id ((b+2)+3)) [1] 0 =
foldr step (\c -> (\b -> id ((b+2)+3)) (c+1)) [] 0 =
foldr step (\c -> id (((c+1)+2)+3)) [] 0 =
(\c -> id (((c+1)+2)+3)) 0 = ...
This answer makes the definition below easily understood in three step.
-- 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)
Step 1. transform the fold of function evaluation to function combination
foldl f z [x1 .. xn] = z & f1 & .. & fn = fn . .. . f1 z. in which fi = \z -> f z xi.
(By using z & f1 & f2 & .. & fn it means fn ( .. (f2 (f1 z)) .. ).)
Step 2. express the function combination in a foldr manner
foldr (.) id [f1 .. fn] = (.) f1 (foldr (.) id [f2 .. fn]) = f1 . (foldr (.) id [f2 .. fn]). Unfold the rest to get foldr (.) id [f1 .. fn] = f1 . .. . fn.
Noticing that the sequence is reversed, we should use the reversed form of (.). Define rc f1 f2 = (.) f2 f1 = f2 . f1, then foldr rc id [f1 .. fn] = rc f1 (foldr (.) id [f2 .. fn]) = (foldr (.) id [f2 .. fn]) . f1. Unfold the rest to get foldr rc id [f1 .. fn] = fn . .. . f1.
Step 3. transform the fold on function list to the fold on operand list
Find step that makes foldr step id [x1 .. xn] = foldr rc id [f1 .. fn]. It is easy to find step = \x g z -> g (f z x).
In 3 steps, the definition of foldl using foldr is clear:
foldl f z xs
= fn . .. . f1 z
= foldr rc id fs z
= foldr step id xs z
Prove the correctness:
foldl f z xs = foldr (\x g z -> g (f z x)) id xs z
= step x1 (foldr step id [x2 .. xn]) z
= s1 (foldr step id [x2 .. xn]) z
= s1 (step x2 (foldr step id [x3 .. xn])) z
= s1 (s2 (foldr step id [x3 .. xn])) z
= ..
= s1 (s2 (.. (sn (foldr step id [])) .. )) z
= s1 (s2 (.. (sn id) .. )) z
= (s2 (.. (sn id) .. )) (f z x1)
= s2 (s3 (.. (sn id) .. )) (f z x1)
= (s3 (.. (sn id) .. )) (f (f z x1) x2)
= ..
= sn id (f (.. (f (f z x1) x2) .. ) xn-1)
= id (f (.. (f (f z x1) x2) .. ) xn)
= f (.. (f (f z x1) x2) .. ) xn
in which xs = [x1 .. xn], si = step xi = \g z -> g (f z xi)
If you find anything to be unclear, please add a comment. :)