First thing, I understand (almost) fold functions. Given the function I can work out easily what will happen and how to use it.
The question is about the way it is implemented which leads to slight difference in the function definition which took some time to understand.To make matters worse most example for folds have same type of the list and default case, which does not help in the understranding as these can be different.
Usage:
foldr f a xs
foldl f a xs
where a is the default case
definition:
foldr: (a -> b -> b) -> b -> [a] -> b
foldl: (a -> b -> a) -> a -> [b] -> a
In definition I understand a
is the first variable to be passed and b
second variable to be passed to function.
Eventually I understood that this is happening due to the fact that when f
finally gets evaluated in foldr
it is implemented as f x a
(i.e. default case is passed as second parameter). But for foldl
it is implemented as f a x
(i.e. default case is passed as first parameter).
Would not the function definition be same if we had passed the default case as same (either 1st parameter in both or 2nd) in both cases? Was there any particular reason for this choice?
To make things a little clearer, I will rename a couple type variables in your foldl
signature...
foldr: (a -> b -> b) -> b -> [a] -> b
foldl: (b -> a -> b) -> b -> [a] -> b
... so that in both cases a
stands for the type of the list elements, and b
for that of the fold results.
The key difference between foldr
and foldl
can be seen by expanding their recursive definitions. The applications of f
in foldr
associate to the right, and the initial value shows up to the right of the elements:
foldr f a [x,y,z] = x `f` (y `f` (z `f` a))
With foldl
, it is the other way around: the association is to the left, and the initial value shows up to the left (as Silvio Mayolo emphasises in his answer, that's how it has to be so that the initial value is in the innermost sub-expression):
foldl f a [x,y,z] = ((a `f` x) `f` y) `f` z
That explains why the list element is the first argument to the function given to foldr
, and the second to the one given to foldl
. (One might, of course, give foldl
the same signature of foldr
and then use flip f
instead of f
when defining it, but that would achieve nothing but confusion.)
P.S.: Here is a good, simple example of folds with the types a
and b
different from each other:
foldr (:) [] -- id
foldl (flip (:)) [] -- reverse