foldr :: (a -> b -> b) -> b -> [a] -> b
foldr f v [] = v
foldr f v (x:xs) = f x (foldr f v xs)
foldl :: (a -> b -> a) -> a -> [b] -> a
foldl f v [] = v
foldl f v (x:xs) = foldl f (f v x) xs
I am trying to wrap my head around this two functions. I have two questions. One regarding function f. In general,
foldr f v xs
f has access to the first element of xs and the recursively processed tail. Here:
foldl f v xs
f has access to the last element of xs and the recursively processed tail.
Is this an useful (and correct) way to think about it ?
My second question is related to fold "right" or "left". In many places, they say that foldr "starts from the right". For example, if I expand the expression
foldr (+) 0 [1,2,3]
I get
(+) 1 (foldr (+) 0 [2,3])
So, I see it is "starting from the left" of the list. The first element and the recursively processed tail are the arguments to the function. Could someone give some light into this issue ?
EDIT: One of my question focuses is on the function f passed to fold; the linked answer doesn't address that point.
"Starting from the right" is good basic intuition, but it can also mislead, as you've already just discovered. The truth of the matter is that lists in Haskell are singly linked and we only have access to one side directly, so in some sense every list operation in Haskell "starts" from the left. But what it does from there is what's important. Let's finish expanding your foldr example.
foldr (+) 0 [1, 2, 3]
1 + foldr 0 [2, 3]
1 + (2 + foldr 0 [3])
1 + (2 + (3 + foldr 0 []))
1 + (2 + (3 + 0))
Now the same for foldl.
foldl (+) 0 [1, 2, 3]
foldl (+) (0 + 1) [2, 3]
foldl (+) ((0 + 1) + 2) [3]
foldl (+) (((0 + 1) + 2) + 3) []
((0 + 1) + 2) + 3
In the foldr case, we make our recursive call directly, so we take the head and make it an argument to our accumulating function, and then we make the other argument our recursive call.
In the foldl case, we make our recursive call by changing the the accumulator argument. Since we're changing the argument rather than the result, the order of evaluation gets flipped around.
The difference is in the way the parentheses "associate". In the foldr case, the parentheses associate to the right, while in the foldl case they associate to the left. Likewise, the "initial" value is on the right for foldr and on the left for foldl.
The general advice for the use of folds on lists in Haskell is this.
Use foldr if you want lazy evaluation that respects the list structure. Since foldr does its recursion inside the function call, so if the folding function happens to be guarded (i.e. by a data constructor), then our foldr call is guarded. For instance, we can use foldr to efficiently construct an infinite list out of another infinite list.
Use foldl' (note the ' at the end), the strict left fold, for situations where you want the operation to be strict. foldl' forces each step of the fold to weak head normal form before continuing, preventing thunks from building up. So whereas foldl will build up the entire internal expression and then potentially evaluate it at the end, foldl' will do the work as we go, which saves a ton of memory on large lists.
Don't use foldl on lists. The laziness gained by foldl is almost never useful, since the only way to get anything useful out of a left fold is to force the whole fold anyway, building up the thunks internally is not useful.
For other data structures which are not right-biased, the rules may be different. All of this is running on the assumption that your Foldable is a Haskell list.
Related
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
A fold is a type of catamorphism, or a way of "tearing down" a data structure into a scalar. In our case, we "tear down" a list. Now, when working with a catamorphism, we need to have a case for each data constructor. Haskell lists have two data constructors.
[] :: [a]
(:) :: a -> [a] -> [a]
That is, [] is a constructor which takes no arguments and produces a list (the empty list). (:) is a constructor which takes two arguments and makes a list, prepending the first argument onto the second. So we need to have two cases in our fold. foldr is the direct example of a catamorphism.
foldr :: (a -> b -> b) -> b -> [a] -> b
The first function will be called if we encounter the (:) constructor. It will be passed the first element (the first argument to (:)) and the result of the recursive call (calling foldr on the second argument of (:)). The second argument, the "default case" as you call it, is for when we encounter the [] constructor, in which case we simply use the default value itself. So it ends up looking like this
foldr (+) 4 [1, 2, 3]
1 + (2 + (3 + 4))
Now, could we have designed foldl the same way? Sure. foldl isn't (exactly) a catamorphism, but it behaves like one in spirit. In foldr, the default case is the innermost value; it's only used at the "last step" of the recursion, when we've run out of list elements. In foldl, we do the same thing for consistency.
foldl (+) 4 [1, 2, 3]
((4 + 1) + 2) + 3
Let's break that down in more detail. foldl can be thought of as using an accumulator to get the answer efficiently.
foldl (+) 4 [1, 2, 3]
foldl (+) (4 + 1) [2, 3]
foldl (+) ((4 + 1) + 2) [3]
foldl (+) (((4 + 1) + 2) + 3) []
-- Here, we've run out of elements, so we use the "default" value.
((4 + 1) + 2) + 3
So I suppose the short answer to your question is that it's more consistent (and more useful), mathematically speaking, to make sure the base case is always at the innermost position in the recursive call, rather than focusing on it being on the left or the right all the time.
Consider the calls foldl (+) 0 [1,2,3,4] and foldr (+) 0 [1,2,3,4] and try to visualize what they do:
foldl (+) 0 [1,2,3,4] = ((((0 + 1) + 2) + 3) + 4)
foldr (+) 0 [1,2,3,4] = (0 + (1 + (2 + (3 + 4))))
Now, let's try to swap the arguments to the call to (+) in each step:
foldl (+) 0 [1,2,3,4] = (4 + (3 + (2 + (1 + 0))))
Note that despite the symmetry this is not the same as the previous foldr. We are still accumulating from the left of the list, I've just changed the order of operands.
In this case, because addition is commutative, we get the same result, but if you try to fold over some non-commutative function, e.g. string concatenation, the result is different. Folding over ["foo", "bar", "baz"], you would obtain "foobarbaz" or "bazbarfoo" (while a foldr would result in "foobarbaz" as well because string concatenation is associative).
In other words, the two definitions as they are make the two functions have the same result for commutative and associative binary operations (like common arithmetic addition/multiplication). Swapping the arguments to the accumulating function breaks this symmetry and forces you to use flip to recover the symmetric behavior.
The two folds yield different results due to their opposite associativity. The base value always shows up within the inner most parens. List traversal happens the same way for both folds.
right fold with (+) using the prefix notation
foldr (+) 10 [1,2,3]
=> + 1 (+ 2 (+ 3 10))
=> + 1 (+ 2 13)
=> + 1 15
=> 16
foldl (+) 10 [1,2,3]
=> + (+ (+ 10 1) 2) 3
=> + (+ 11 2) 3
=> + 13 3
=> 16
both folds evaluate to the same result because (+) is commutative, i.e.
+ a b == + b a
lets see what happens when the function is not commutative, e.g. division or exponentiation
foldl (/) 1 [1, 2, 3]
=> / (/ (/ 1 1) 2) 3
=> / (/ 1 2) 3
=> / 0.5 3
=> 0.16666667
foldr (/) 1 [1, 2, 3]
=> / 1 (/ 2 (/ 3 1))
=> / 1 (/ 2 3)
=> / 1 0.666666667
=> 1.5
now, lets evaluate foldr with function flip (/)
let f = flip (/)
foldr f 1 [1, 2, 3]
=> f 1 (f 2 (f 3 1))
=> f 1 (f 2 0.3333333)
=> f 1 0.16666667
=> 0.16666667
similarly, lets evaluate foldl with f
foldl f 1 [1, 2, 3]
=> f (f (f 1 1) 2) 3
=> f (f 1 2) 3
=> f 2 3
=> 1.5
So, in this case, flipping the order of the arguments of the folding function can make left fold return the same value as a right fold and vice versa. But that is not guaranteed. Example:
foldr (^) 1 [1, 2, 3] = 1
foldl (^) 1 [1, 2, 3] = 1
foldr (flip (^)) 1 [1,2,3] = 1
foldl (flip (^)) 1 [1,2,3] = 9 -- this is the odd case
foldl (flip (^)) 1 $ reverse [1,2,3] = 1
-- we again get 1 when we reverse this list
incidentally, reverse is equivalent to
foldl (flip (:)) []
but try defining reverse using foldr
I've looked at different folds and folding in general as well as a few others and they explain it fairly well.
I'm still having trouble on how it would work in this case.
length :: [t] -> Int
length list = foldr (+) 0 (map (\x ->1) list)
Could someone go through that step by step and try to explain that to me.
And also how would foldl work as well.
(map (\x ->1) list) takes the list and turns it into a list of 1 values:
(map (\x ->1) ["a", "b", "c"]) == [1, 1, 1]
Now, if you substitute that in the original foldr, it looks like this:
foldr (+) 0 [1, 1, 1]
The starting point is 0 and the aggregation function is (+). As it steps through each element in the list, you are basically adding up all the 1 values, and that's how you end up returning the length.
foldr starts from the right and works back to the head of the list. foldl starts from the left and works through the list. Because the aggregation function is (+) :: Num a => a -> a -> a, the ordering of the left and right arguments in (+) is logically inconsequential (with the caveat that foldl has stack overflow problems with large lists because of lazy evaluation)
In foldl definition possible wrong in SML/NJ 110.75, I found that the relation foldl (op -) 2 [1] = foldr (op -) 2 [1] holds. But when I tried the above in Haskell I found that the above relation rewritten in Haskell as foldl (-) 2 [1] == foldr (-) 2 [1] doesn't hold. Why is this? Does Haskell have different definition for fold than SML/NJ?
Thanks
In ML, both folds have the same type signature:
val foldl : ('a * 'b -> 'b) -> 'b -> 'a list -> 'b
val foldr : ('a * 'b -> 'b) -> 'b -> 'a list -> 'b
whereas in Haskell they're different:
foldl :: (a -> b -> a) -> a -> [b] -> a
foldr :: (a -> b -> b) -> b -> [a] -> b
so Haskell's foldl is necessarily doing something different with the operation it's been given.
Similarities
The two languages agree on both the type and the value computed by foldr - a list folded into a value by moving righwards along the list, bracketed from the right hand end:
foldr f init [x1, x2, ..., xn]
==> f(x1, f(x2, ..., f(xn, init)...))
Differences
First, ML has
foldl f init [x1, x2, ..., xn]
==> f(xn,...,f(x2, f(x1, init))...)
So ML's foldl is a left fold in the sense that it folds the list leftwards instead of rightwards.
whereas in Haskell, you have
foldl f init [x1,x2,.....,xn]
==> f(f(...f(f(init,x1),x2),.....),xn)
In haskell, foldl is a left fold in the sense that it puts the initial value at the left and brackets the list from the left, but retains its order.
Your example
With a list with just a single element, ML does f(x1,init) which gives you x1 - init which happens to be the same as foldr's xn - init because the first and last elements are the same.
Conversely, Haskell does f(init,x1) which gives you init - x1. That's why you get the opposite answer.
Slightly longer example
ML's foldl:
foldl (op -) 100 [1,2,3,4]
==> 4 - (3 - (2 - (1 - 100)))
==> 102
ML/Haskell's foldr:
foldr (-) 100 [1,2,3,4] or foldr (op -) 100 [1,2,3,4]
==> 1 - (2 - (3 - (4 - 100)))
==> 98
Haskell's foldl:
foldl (-) 100 [1,]
==> (((100 - 1) - 2) - 3) - 4
==> 90
Conclusion
Yes the two definitions are different for foldl. ML's left means opposite order of elements, whereas Haskell's left means opposite order of bracketing.
This isn't a big problem as long as you remember which one you're using. (If the types of init and x1 are different, the type checker will tell you when you get it wrong.)
Does this help?
mlFoldl :: (a -> b -> b) -> b -> [a] -> b
mlFoldl f = foldl (flip f)
Long story short, they are essentially the same, with one minor difference: the order of the arguments passed to the operator (the combining function you pass to fold) are flipped. And since subtraction is not commutative, it will produce different results.
In Haskell (as well as OCaml, C++, Clojure, Common Lisp, Erlang, F#, JavaScript, PHP, Python, Ruby, Scala, and many others), for foldl, the supplied function's first argument is the initial value, or the "folded value so far", while the second argument is an element from the list.
However, in Standard ML, the supplied function's first argument is the element from the list, and the second argument is the initial value, or the "folded value so far".
Neither is "correct" or "incorrect". The order of arguments is purely a design decision. The way Haskell does it is more commonly used today across languages. And in a certain "graphical" way of looking at folding, it makes more sense. Why did SML define theirs the way they did? I am not sure. Perhaps so that the signatures of foldl and foldr will be the same.
Expanding on Some Other Guy's answer:
From the Haskell Wiki:
-- if the list is empty, the result is the initial value z; else
-- apply f to the first element and the result of folding the rest
foldr f z [] = z
foldr f z (x:xs) = f x (foldr f z xs)
-- if the list is empty, the result is the initial value; else
-- we recurse immediately, making the new initial value the result
-- of combining the old initial value with the first element.
foldl f z [] = z
foldl f z (x:xs) = foldl f (f z x) xs
so foldl (-) 2 [1] is (2 - 1) and foldr (-) 2 [1] is (1 - 2)
From the SML Basis Library
foldl f init [x1, x2, ..., xn]
returns
f(xn,...,f(x2, f(x1, init))...)
or init if the list is empty.
foldr f init [x1, x2, ..., xn]
returns
f(x1, f(x2, ..., f(xn, init)...))
or init if the list is empty.
so foldl (op -) 2 [1] is fxn - init or 1 - 2, and foldr (op -) 2 [1] is fx1 - init. It is still 1 - 2, but only by coincidence. The answers diverge with a longer list, but not as much as the answers between Haskell and SML.
We have been asked to answer whether foldr or foldl is more efficient.
I am not sure, but doesn't it depend on what I am doing, especially what I want to reach with my functions?
Is there a difference from case to case or can one say that foldr or foldl is better , because...
Is there a general answer ?
Thanks in advance!
A fairly canonical source on this question is Foldr Foldl Foldl' on the Haskell Wiki. In summary, depending on how strictly you can combine elements of the list and what the result of your fold is you may decide to choose either foldr or foldl'. It's rarely the right choice to choose foldl.
Generally, this is a good example of how you have to keep in mind the laziness and strictness of your functions in order to compute efficiently in Haskell. In strict languages, tail-recursive definitions and TCO are the name of the game, but those kinds of definitions may be too "unproductive" (not lazy enough) for Haskell leading to the production of useless thunks and fewer opportunities for optimization.
When to choose foldr
If the operation that consumes the result of your fold can operate lazily and your combining function is non-strict in its right argument, then foldr is usually the right choice. The quintessential example of this is the nonfold. First we see that (:) is non-strict on the right
head (1 : undefined)
1
Then here's nonfold written using foldr
nonfoldr :: [a] -> [a]
nonfoldr = foldr (:) []
Since (:) creates lists lazily, an expression like head . nonfoldr can be very efficient, requiring just one folding step and forcing just the head of the input list.
head (nonfoldr [1,2,3])
head (foldr (:) [] [1,2,3])
head (1 : foldr (:) [] [2,3])
1
Short-circuiting
A very common place where laziness wins out is in short-circuiting computations. For instance, lookup :: Eq a => a -> [a] -> Bool can be more productive by returning the moment it sees a match.
lookupr :: Eq a => a -> [a] -> Bool
lookupr x = foldr (\y inRest -> if x == y then True else inRest) False
The short-circuiting occurs because we discard isRest in the first branch of the if. The same thing implemented in foldl' can't do that.
lookupl :: Eq a => a -> [a] -> Bool
lookupl x = foldl' (\wasHere y -> if wasHere then wasHere else x == y) False
lookupr 1 [1,2,3,4]
foldr fn False [1,2,3,4]
if 1 == 1 then True else (foldr fn False [2,3,4])
True
lookupl 1 [1,2,3,4]
foldl' fn False [1,2,3,4]
foldl' fn True [2,3,4]
foldl' fn True [3,4]
foldl' fn True [4]
foldl' fn True []
True
When to choose foldl'
If the consuming operation or the combining requires that the entire list is processed before it can proceed, then foldl' is usually the right choice. Often the best check for this situation is to ask yourself whether your combining function is strict---if it's strict in the first argument then your whole list must be forced anyway. The quintessential example of this is sum
sum :: Num a => [a] -> a
sum = foldl' (+) 0
since (1 + 2) cannot be reasonably consumed prior to actually doing the addition (Haskell isn't smart enough to know that 1 + 2 >= 1 without first evaluating 1 + 2) then we don't get any benefit from using foldr. Instead, we'll use the strict combining property of foldl' to make sure that we evaluate things as eagerly as needed
sum [1,2,3]
foldl' (+) 0 [1,2,3]
foldl' (+) 1 [2,3]
foldl' (+) 3 [3]
foldl' (+) 6 []
6
Note that if we pick foldl here we don't get quite the right result. While foldl has the same associativity as foldl', it doesn't force the combining operation with seq like foldl' does.
sumWrong :: Num a => [a] -> a
sumWrong = foldl (+) 0
sumWrong [1,2,3]
foldl (+) 0 [1,2,3]
foldl (+) (0 + 1) [2,3]
foldl (+) ((0 + 1) + 2) [3]
foldl (+) (((0 + 1) + 2) + 3) []
(((0 + 1) + 2) + 3)
((1 + 2) + 3)
(3 + 3)
6
What happens when we choose wrong?
We get extra, useless thunks (space leak) if we choose foldr or foldl when in foldl' sweet spot and we get extra, useless evaluation (time leak) if we choose foldl' when foldr would have been a better choice.
nonfoldl :: [a] -> [a]
nonfoldl = foldl (:) []
head (nonfoldl [1,2,3])
head (foldl (:) [] [1,2,3])
head (foldl (:) [1] [2,3])
head (foldl (:) [1,2] [3]) -- nonfoldr finished here, O(1)
head (foldl (:) [1,2,3] [])
head [1,2,3]
1 -- this is O(n)
sumR :: Num a => [a] -> a
sumR = foldr (+) 0
sumR [1,2,3]
foldr (+) 0 [1,2,3]
1 + foldr (+) 0 [2, 3] -- thunks begin
1 + (2 + foldr (+) 0 [3])
1 + (2 + (3 + foldr (+) 0)) -- O(n) thunks hanging about
1 + (2 + (3 + 0)))
1 + (2 + 3)
1 + 5
6 -- forced O(n) thunks
In languages with strict/eager evaluation, folding from the left can be done in constant space, while folding from the right requires linear space (over the number of elements of the list). Because of this, many people who first approach Haskell come over with this preconception.
But that rule of thumb doesn't work in Haskell, because of lazy evaluation. It's possible in Haskell to write constant space functions with foldr. Here is one example:
find :: (a -> Bool) -> [a] -> Maybe a
find p = foldr (\x next -> if p x then Just x else next) Nothing
Let's try hand-evaluating find even [1, 3, 4]:
-- The definition of foldr, for reference:
foldr f z [] = z
foldr f z (x:xs) = f x (foldr f z xs)
find even (1:3:4:[])
= foldr (\x next -> if even x then Just x else next) (1:3:4:[])
= if even 1 then Just 1 else foldr (\x next -> if even x then Just x else next) (3:4:[])
= foldr (\x next -> if even x then Just x else next) (3:4:[])
= if even 3 then Just 3 else foldr (\x next -> if even x then Just x else next) (4:[])
= foldr (\x next -> if even x then Just x else next) (4:[])
= if even 4 then Just 4 else foldr (\x next -> if even x then Just x else next) []
= Just 4
The size of the expressions in the intermediate steps has a constant upper bound—this actually means that this evaluation can be carried out in constant space.
Another reason why foldr in Haskell can run in constant space is because of the list fusion optimizations in GHC. GHC in many cases can optimize a foldr into a constant-space loop over a constant-space producer. It cannot generally do that for a left fold.
Nonetheless, left folds in Haskell can be written to use tail recursion, which can lead to performance benefits. The thing is that for this to actually succeed you need to be very careful about laziness—naïve attempts at writing a tail recursive algorithm normally lead to linear-space execution, because of an accumulation of unevaluated expressions.
Takeaway lessons:
When you're starting out in Haskell, try to use library functions from Prelude and Data.List as much as possible, because they've been carefully written to exploit performance features like list fusion.
If you need to fold a list, try foldr first.
Never use foldl, use foldl' (the version that avoids unevaluated expressions).
If you want to use tail-recursion in Haskell, first you need to understand how evaluation works—otherwise you may make things worse.
(Please read the comments on this post. Some interesting points were made and what I wrote here isn't completely true!)
It depends. foldl is usually faster since it's tail recursive, meaning (sort of), that all computation is done in-place and there's no call-stack. For reference:
foldl f a [] = a
foldl f a (x:xs) = foldl f (f a x) xs
To run foldr we do need a call stack, since there is a "pending" computation for f.
foldr f a [] = a
foldr f a (x:xs) = f x (foldr f a xs)
On the other hand, foldr can short-circuit if f is not strict in its first argument. It's lazier in a way. For example, if we define a new product
prod 0 x = 0
prod x 0 = 0
prod x y = x*y
Then
foldr prod 1 [0...n]
Takes constant time in n, but
foldl prod 1 [0...n]
takes linear time. (This will not work using (*), since it does not check if any argument is 0. So we create a non-strict version. Thanks to Ingo and Daniel Lyons for pointing it out in the comments)
Learning Haskell, I came across the fact that foldl creates thunks and might crash the stack, so it's better to use foldl' from Data.List. Why is it just foldl, and not, for example, foldr?
Thanks
There is no need for foldr' because you can cause the effect yourself.
Here is why: Consider foldl f 0 [1,2,3]. This expands to f (f (f 0 1) 2) 3, so by the time you get anything back to work with, thunks for (f 0 1) and (f (f 0 1) 2) have to be created. If you want to avoid this (by evaluating these subexpressions before continuing), you have to instruct foldl to do it for you – that is foldl'.
With foldr, things are different. What you get back from foldr f 0 [1, 2, 3] is f 1 (foldr f 0 [2, 3]) (where the expression in parenthesis is a thunk). If you want to evaluate (parts of) the outer application of f, you can do that now, without a linear number of thunks being created first.
But in general, you are using foldr with lazy functions for f that can already do something (e.g. produce list constructors) before looking at the second argument.
Using foldr with a strict f (e.g. (+)) has the unwanted effect of putting all applications on the stack until the end of the list is reached; clearly not what you want, and not a situation where a however-looking foldr' could help.