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)
Related
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.
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
When using foldr, the recursion occours inside the function, so,
when the given function doesn't strictly evaluate both sides, and
can return based on the first one, foldr must be a good solution,
because it will work on infinity lists
findInt :: Int -> [Int] -> Bool
findInt z [] = False
-- The recursion occours inside de given function
findInt z (x:xs)
| z == x = True
| otherwise = findInt z xs
equivalent to:
findInt' :: Int -> [Int] -> Bool
findInt' z = foldr (\x r -> if z == x then True else r) False
-- Where False is the "default value" (when it finds [], ex: findInt z [] = False)
A situation when foldr is not appropriate:
addAll :: Int -> [Int] -> Int
addAll z [] = z
-- The recursion occours outside the given function (+)
addAll z (x:xs) = addAll (z + x) xs
In this case, because + is strict (needs to evaluate both sides to return)
it would be greately useful if we applied it in some way which we could
have a redex (reducible expression), to make it possible to avoid thunks
and (when forced to run with previous evaluation, not lazy) in constant
space and without pushing to much onto the stack
(similar to the advantages of a for loop in imperative algorithms)
addAll' :: Int -> [Int] -> Int
addAll' z [] = z
addAll' z (x:xs) = let z' = z + x
in seq z' $ addAll' z' xs
equivalent to:
addAll'' :: Int -> [Int] -> Int
addAll'' z = foldl' (+) z
In this little case, using foldr (inside recursion) doesn't make sense
because it wouldn't make redexes.
It would be like this:
addAll''' :: Int -> [Int] -> Int
addAll''' z [] = z
addAll''' z (x:xs) = (+) x $ addAll''' z xs
The main objective of this question is first, know whether my premises are
right or where they could be better and second, help to make it more clear
for others who are also learning Haskell the differences between inside and
outside recursion, among the approaches, to have it clear in mind which one
could be more appropriated to a given situation
Helpful links:
Haskell Wiki
Stackoverflow - Implications of foldr vs. foldl (or foldl')
Aside from the fact that foldr is the natural catamorphism of a list, while foldl and foldl' are not, a few guidelines for their use:
you are correct on that foldr will always return, even on infinite lists, as long as the function is non-strict in its second argument, since the elements of the list are made available to the first argument of the function immediately (as opposed to foldl and foldl', where the elements of the list are not available to the first argument of the function until the list has been entirely consumed);
foldl' will be a better choice for non-infinite lists if you want to ensure constant space, since it's tail recursive, but it will always parse the entire list regardless of the strictness in the evaluation of the arguments to the function passed to it;
in general, foldr is equivalent to recursion, while foldl and foldl' are analogous to loops;
because of the fact that foldr is the natural catamorphism, if your function needs to recreate the list (for example, if your function is just the list constructor ':'), foldr would be more adequate;
with respect to foldl vs. foldl', foldl' is usually preferable because it will not build a huge thunk but, if the function passed to it is non strict in its first argument and the list is not infinite, foldl may return while foldl' may give an error (there is a good example in the Haskell wiki).
As a side note, I believe that you are using the term "inside recursion" to define foldr and "outside recursion" for foldl and foldl', but I haven't seen these terms before in the literature. More commonly these functions are just referred to as folding from the right and folding from the left respectively, terms that while may not be exactly correct, they give a good notion of the order in which the elements of the list are passed to the function.
I'm trying to define a function in Haskell using the foldr function:
fromDigits :: [Int] -> Int
This function takes a list of Ints (each on ranging from 0 to 9) and converts to a single Int. For example:
fromDigits [0,1] = 10
fromDigits [4,3,2,1] = 1234
fromDigits [2,3,9] = 932
fromDigits [2,3,9,0,1] = 10932
Anyway, I have no trouble defining this using explicit recursion or even using zipWith:
fromDigits n = sum (zipWith (*) n (map ((^)10) [0..]))
But now I have to define it using a foldr, but I don't know how to get the powers of 10. What I have is:
fromDigits xs = foldr (\x acc -> (x*10^(???)) + acc) 0 xs
How can I get them to decrease? I know I can start with (length xs - 1) but what then?
Best Regards
You were almost there:
your
fromDigits xs = foldr (\x acc -> (x*10^(???)) + acc) 0 xs
is the solution with 2 little changes:
fromDigits = foldr (\x acc -> acc*10 + x) 0
(BTW I left out the xs on each sides, that's not necessary.
Another option would be
fromDigits = foldl (\x acc -> read $ (show x) ++ (show acc)) 0
The nice thing about foldr is that it's so extemely easy to visualise!
foldr f init [a,b, ... z]
≡ foldr f init $ a : b : ... z : []
≡ a`f b`f`... z`f`init
≡ f a (f b ( ... (f z init)...)))
so as you see, the j-th list element is used in j consecutive calls of f. The head element is merely passed once to the left of the function. For you application, the head element is the last digit. How should that influence the outcome? Well, it's just added to the result, isn't it?
15 = 10 + 5
623987236705839 = 623987236705830 + 9
– obvious. Then the question is, how do you take care for the other digits? Well, to employ the above trick you first need to make sure there's a 0 in the last place of the carried subresult. A 0 that does not come from the supplied digits! How do you add such a zero?
That should really be enough hint given now.
The trick is, you don't need to compute the power of 10 each time from scratch, you just need to compute it based on the previous power of ten (i.e. multiply by 10). Well, assuming you can reverse the input list.
(But the lists you give above are already in reverse order, so arguably you should be able to re-reverse them and just say that your function takes a list of digits in the correct order. If not, then just divide by 10 instead of multiplying by 10.)
This is my take version using foldr:
myTake n list = foldr step [] list
where step x y | (length y) < n = x : y
| otherwise = y
main = do print $ myTake 2 [1,2,3,4]
The output is not what I expect:
[3,4]
I then tried to debug by inserting the length of y into itself and the result was:
[3,2,1,0]
I don't understand why the lengths are inserted in decreasing order. Perhaps something obvious I missed?
If you want to implement take using foldr you need to simulate traversing the list from left to right. The point is to make the folding function depend on an extra argument which encodes the logic you want and not only depend on the folded tail of the list.
take :: Int -> [a] -> [a]
take n xs = foldr step (const []) xs n
where
step x g 0 = []
step x g n = x:g (n-1)
Here, foldr returns a function which takes a numeric argument and traverses the list from left to right taking from it the amount required. This will also work on infinite lists due to laziness. As soon as the extra argument reaches zero, foldr will short-circuit and return an empty list.
foldr will apply the function step starting from the *last elements**. That is,
foldr step [] [1,2,3,4] == 1 `step` (2 `step` (3 `step` (4 `step` [])))
== 1 `step` (2 `step` (3 `step` (4:[])))
== 1 `step` (2 `step (3:4:[])) -- length y == 2 here
== 1 `step` (3:4:[])
== 3:4:[]
== [3, 4]
The lengths are "inserted" in decreasing order because : is a prepending operation. The longer lengths are added to the beginning of the list.
(Image taken from http://en.wikipedia.org/wiki/Fold_%28higher-order_function%29)
*: For simplicity, we assume every operation is strict, which is true in OP's step implementation.
The other answers so far are making it much too complicated, because they seem excessively wedded to the notion that foldr works "from right to left." There is a sense in which it does, but Haskell is a lazy language, so a "right to left" computation that uses a lazy fold step will actually be executed from left to right, as the result is consumed.
Study this code:
take :: Int -> [a] -> [a]
take n xs = foldr step [] (tagFrom 1 xs)
where step (a, i) rest
| i > n = []
| otherwise = a:rest
tagFrom :: Enum i => i -> [a] -> [(a, i)]
tagFrom i xs = zip xs [i..]