Understanding the work of foldr and unfoldr - haskell

I have hard time understanding how these bits of code work.
"Map" function must apply the function to all elements in given list, and generate list consist of results of applying. So we are giving our function f and some list, then in lambda expression our list transforms into head "x" and tail "xs", we applying function "f" to x and append it to "xs". But what happens next? How and what exactly foldr takes for its second argument (which must be some starting value usually). And for what purpose empty list?
And function "rangeTo" : we are creating lambda expression, where we are checking that we are over the end of range, end if we are than we are giving Nothing, or if we are not at end, we are giving pair where first number append to resulting list, and second number used as next value for "from". Is it all what happens in this function, or I'm missing something?
--custom map function through foldr
map :: (a -> b) -> [a] -> [b]
map f = foldr (\x xs -> f x : xs) []
--function to create list with numbers from first argument till second and step "step"
rangeTo :: Integer -> Integer -> Integer -> [Integer]
rangeTo from to step = unfoldr (\from -> if from >= to then Nothing else Just (from, from+step)) from

To understand How foldr operates on a list. It is better to write down the definition of foldr as
foldr step z xs
= x1 `step` foldr step z xs1 -- where xs = x:xs1
= x1 `step` (x2 `step` foldr step z xs2) -- where xs = x1:x2:xs2
= x1 `step` (x2 `step` ... (xn `step` foldr step z [])...) -- where xs = x1:x2...xn:[]
and
foldr step z [] = z
For your case:
foldr (\x xs -> f x : xs) []
where
step = (\x xs -> f x : xs)
z = []
From the definition of foldr, the innermost expression
(xn `step` foldr step z [])
is evaluated first, that is
xn `step` foldr step z []
= step xn (foldr step z [])
= step xn z
= step xn [] -- z = []
= f xn : [] -- step = (\x xs -> f x : xs)
= [f xn]
what happens next? The evaluation going on as
x(n-1) `step` (xn `step` foldr step z [])
= step x(n-1) [f xn]
= f x(n-1) : [f xn]
= [f x(n-1), f xn]
untill:
x1 `step` (x2 ...
= step x1 [f x2, ..., f xn]
= [f x1, f x2, ... f xn]

So we are giving our function f and some list, then in lambda expression our list transforms into head "x" and tail "xs", we applying function "f" to x and append it to "xs".
This is not the case. Look closely at the implementation:
map :: (a -> b) -> [a] -> [b]
map f = foldr (\x xs -> f x : xs) []
There is an implied variable here, we can add it back in:
map :: (a -> b) -> [a] -> [b]
map f ls = foldr (\x xs -> f x : xs) [] ls
map takes two arguments, a function f and a list ls. It passes ls to foldr as the list to fold over, and it passes [] as the starting accumulator value. The lambda takes a list element x and an accumulator xs (initially []), and returns a new accumulator f x : xs. It does not perform a head or tail anywhere; x and xs were never part of the same list.
Let's step through the evaluation to see how this function works:
map (1+) [2, 4, 8]
foldr (\x xs -> (1+) x : xs) [] [2, 4, 8] -- x = 8, xs = []
foldr (\x xs -> (1+) x : xs) [9] [2, 4] -- x = 4, xs = [9]
foldr (\x xs -> (1+) x : xs) [5, 9] [2] -- x = 2, xs = [5, 9]
foldr (\x xs -> (1+) x : xs) [3, 5, 9] [] -- xs = [3, 5, 9]
map (1+) [2, 4, 8] == [3, 5, 9]
The empty list accumulates values passed through f, starting from the right end of the input list.
And function "rangeTo" : we are creating lambda expression, where we are checking that we are over the end of range, end if we are than we are giving Nothing, or if we are not at end, we are giving pair where first number append to resulting list, and second number used as next value for "from". Is it all what happens in this function, or I'm missing something?
Yes, that's exactly what's going on. The lambda takes an accumulator, and returns the next value to put in the list and a new accumulator, or Nothing if the list should end. The accumulator in this case is the current value in the list. The list should end if that value is past the end of the range. Otherwise it calculates the next accumulator by adding the step.
Again, we can step through the evaluation:
rangeTo 3 11 2 -- from = 3, to = 11, step = 2
Just (3, 5) -- from = 3
Just (5, 7) -- from = 3 + step = 5
Just (7, 9) -- from = 5 + step = 7
Just (9, 11) -- from = 7 + step = 9
Nothing -- from = 9 + step = 11, 11 >= to
rangeTo 3 11 2 == [3, 5, 7, 9]

Related

Trouble understanding the behavior of `foldr` and `map` in Haskell

I have a function prefixes that, given [1, 2, 3], returns the prefixes [[1], [1, 2], [1, 2, 3]]. It is defined as follows:
prefixes :: Num a => [a] -> [[a]]
prefixes = foldr (\x acc -> [x] : (map ((:) x) acc)) []
I have spent nearly two days trying to understand why this works. When I debug this in my head, I imagine this for prefixes [1, 2, 3]:
foldr call|__________________________________________________________________________
1 | [1] : (map ((:) 1) [])
|
| where x = 1 and acc = []
| returns acc = [[1]]
|
2 | [2] : (map ((:) 2) [[1]])
|
| where x = 2 and acc = [[1]]
| and (map ((:) 2) [[1]])
| returns acc = [[1, 2]]
| and [2] : [[1, 2]]
| returns [[2], [1, 2]]
|
3 | [3] : (map ((:) 3) [[2], [1, 2]])
|
| where x = 3 and acc = [[2], [1, 2]]
| and (map ((:) 3) [[2], [1, 2]])
| returns acc = [[2, 3], [1, 2, 3]]
| and [3] : [[2, 3], [1, 2, 3]]
| returns [[3], [2, 3], [1, 2, 3]]
|
And then the function terminates and returns [[3], [2, 3], [1, 2, 3]]. But obviously that is not happening. It returns [[1], [1, 2], [1, 2, 3]].
In Ghci, I find this:
Stopped in Main.prefixes, ex.hs:21:20-63
_result :: [a] -> [[a]] = _
[ex.hs:21:20-63] *Main> :step
Stopped in Main.prefixes, ex.hs:21:37-59
_result :: [[Integer]] = _
acc :: [[Integer]] = _
x :: Integer = 1
[ex.hs:21:37-59] *Main> :step
[[1]
Stopped in Main.prefixes, ex.hs:21:44-58
_result :: [[Integer]] = _
acc :: [[Integer]] = _
x :: Integer = 1
[ex.hs:21:44-58] *Main> :step
Stopped in Main.prefixes, ex.hs:21:37-59
_result :: [[Integer]] = _
acc :: [[Integer]] = _
x :: Integer = 2
[ex.hs:21:37-59] *Main> :step
,
Stopped in Main.prefixes, ex.hs:21:49-53
_result :: [Integer] -> [Integer] = _
x :: Integer = 1
[ex.hs:21:49-53] *Main> :step
[1,2]
Stopped in Main.prefixes, ex.hs:21:44-58
_result :: [[Integer]] = _
acc :: [[Integer]] = _
x :: Integer = 2
[ex.hs:21:44-58] *Main> :step
Stopped in Main.prefixes, ex.hs:21:37-59
_result :: [[Integer]] = _
acc :: [[Integer]] = _
x :: Integer = 3
[ex.hs:21:37-59] *Main> :step
,
[1Stopped in Main.prefixes, ex.hs:21:49-53
_result :: [Integer] -> [Integer] = _
x :: Integer = 2
[ex.hs:21:49-53] *Main> :step
,2,3]
Stopped in Main.prefixes, ex.hs:21:44-58
_result :: [[Integer]] = _
acc :: [[Integer]] = _
x :: Integer = 3
[ex.hs:21:44-58] *Main> :step
]
Which I interpret as:
__lines___|__________________________________________________________________________
21:37-59 | [1] : (map ((:) 1) acc) -> [[1]
|
|
|
21:44-58 | (map ((:) 1) acc) -> does nothing, as acc = []
|
|
|
21:37-59 | [2] : (map ((:) 2) acc) -> ,
|
|
|
21:49-53 | ((:) 1) -> [1, 2]
|
|
|
21:44-58 | (map ((:) 2) acc) -> outputs nothing
|
|
|
21:37-59 | [3] : (map ((:) 3) acc) -> ,[1
|
|
|
21:49-53 | ((:) 2) -> , 2, 3]
|
|
21:44-58 | (map ((:) 3) acc) -> ]
|
Printing [[1], [1, 2], [1, 2, 3]]. Could someone explain why, when lines 49-53 are evaluated, x is the x value from the previous foldr invocation?
I know that (map ((:) x) acc) can be expanded to (foldr ((:) . ((:) x)) [] acc), as map f = foldr ((:) . f) []. So I rewrote the function into the following
prefixesSolution :: Num a => [a] -> [[a]]
prefixesSolution = foldr (\x acc -> [x] : (foldr ((:) . ((:) x)) [] acc)) []
And this works as well. Now, the lambda passed to the second foldr ((:) . ((:) x)) I would imagine could be refactored as (\ element accumulator -> (element:accumulator) . ((element:accumulator) x)). But this does not work: Couldn't match expected type ‘a -> a0 -> b0’ with actual type ‘[[a]]’. All this I have done in order to pinpoint exactly what is happening.
I also do not understand the function passed to map ((:) x).
I apologize for how convoluted this post is. At this point I don't even know what I don't know. If someone could clearly walk me through this function I would be so so grateful.
foldr accumulates from the end of the list.
Initially acc = [] (using the second argument of foldr).
Starting from the end, we apply the given function \x acc -> [x] : (map ((:) x) acc) with x = 3:
[3] : map (3 :) []
= [[3]]
With acc = [[3]], add the preceding element, x = 2:
[2] : map (2 :) [[3]]
= [[2], [2,3]]
With acc = [[2], [2,3]], add the preceding element, x = 1:
[1] : map (1 :) [[2], [2,3]]
= [[1], [1,2], [1,2,3]]
You can also still evaluate foldr "left to right", but in that case, remember that acc gets instantiated with "the next recursive call".
foldr f b (x : xs) = f x (foldr f b xs)
prefixes [1,2,3]
= [1] : map (1 :) (prefixes [2,3]) -- acc = prefixes [2,3], the next recursive call
= [1] : map (1 :) ([2] : map (2 :) (prefixes [3]))
...
Starting with the question about the function passed to map:
In Haskell, all operators are also functions. By itself, : is the list construction ("cons") operator:
1 : [2,3] -- > [1,2,3]
If you put parentheses around it, it becomes a prefix function instead of an infix operator:
(:) 1 [2,3] -- > [1,2,3]
When you remember that Haskell function application is curried, then you can see that (:) 1 is necessarily a function that prepends 1 to a list:
f = (:) 1
f [2,3] -- > [1,2,3]
So the function passed to map is one that takes a list as its argument and prepends x (the current item from the foldr) to that list.
The surrounding function prepends [x] to the result of that map, growing the list.
Next let's talk about foldr itself. It may help to think of the list [1,2,3] as the sequence of cons calls required to create it. In tree form that looks like this:
(:)
1 (:)
2 (:)
3 []
And in Haskell you could write it like this:
(:) 1 ( (:) 2 ( (:) 3 [] ) )
Given the above, what the call foldr func init [1,2,3] does is replace the final [] with the init value and all the (:)s with the supplied func. So the final result is the same as the result of this expression, which you can think of as an expansion of the foldr version:
func 1 ( func 2 ( func 3 init ) )
That is, foldr first calls the func on 3 (which becomes x) and [] (which becomes acc). (Technically, it calls the function on 3, and the result of that call is another function that it then calls on [], but that's just how function application works in Haskell; the difference is not important to this particular discussion.) Then it calls the func on 2 and the result of the first call, and then it calls it on 1 and the result of the second call.
As we established above, the func first does a map ((:) 3) [] - returning [], since mapping anything across the empty list just returns the empty list - and prepends [3] to the result, giving [[3]].
Then it calls the func on 2 and [[3]]. The map returns [[2,3]], to which it prepends [2], yielding [[2],[2,3]].
Finally it calls the func on 1 and [[2],[2,3]]. The map returns [[1,2],[1,2,3]] and the func prepends [1] to it, yielding the final answer [[1],[1,2],[1,2,3]].
When evaluating something like prefixes [1,2,3] by hand, you should try to be very careful in writing out each step of the evaluation.
I would look at it like this:
Before we start, I suggest a couple of steps of preparation. I'll also give variables fresh names as we go, to hopefully make things more clear.
It will help to write the pattern matches as case expressions, so we will do this next.
We can observe that foldr can be written as
foldr f z list =
case list of
[] -> z
(y:ys) -> f y (foldr f z ys)
I'll skip over some of the details of specific map applications later on and focus more on the foldr steps. If this is unclear, I can expand on that more.
Now that we've got that taken care of, we can evaluate. I'm not going to focus so much on the evaluation order, since this will not affect the final result. This will let me simplify a couple of things. As a result, you shouldn't necessarily assume this is exactly what the computer is doing (even though the result is the same here, it could have differences in terms of memory efficiency, time efficiency and possibly strictness properties).
prefixes [1,2,3]
==> {definition of prefixes}
foldr (\x acc -> [x] : (map ((:) x) acc)) [] [1,2,3]
==> {definition of foldr}
let f = \x acc -> [x] : (map ((:) x) acc)
in
case [1,2,3] of
[] -> []
(y:ys) -> f y (foldr f [] ys)
==> {reduce case match on known value}
let f = \x acc -> [x] : (map ((:) x) acc)
in
f 1 (foldr f [] [2,3])
==> {definition of foldr}
let f = \x acc -> [x] : (map ((:) x) acc)
in
f 1 (case [2,3] of
[] -> []
(y:ys) -> f y (foldr f [] ys))
==> {reduce case match on known value}
let f = \x acc -> [x] : (map ((:) x) acc)
in
f 1 (f 2 (foldr f [] [3]))
==> {definition of foldr}
let f = \x acc -> [x] : (map ((:) x) acc)
in
f 1 (f 2 (case [3] of
[] -> []
(y:ys) -> f y (foldr f [] ys)))
==> {reduce case match on known value}
let f = \x acc -> [x] : (map ((:) x) acc)
in
f 1 (f 2 (f 3 (foldr f [] [])))
==> {definition of foldr}
let f = \x acc -> [x] : (map ((:) x) acc)
in
f 1 (f 2 (f 3 (case [] of
[] -> []
(y:ys) -> f y (foldr f [] ys))))
==> {reduce case match on known value}
let f = \x acc -> [x] : (map ((:) x) acc)
in
f 1 (f 2 (f 3 []))
==> {apply f}
let f = \x acc -> [x] : (map ((:) x) acc)
in
f 1 (f 2 ([3] : map ((:) 3) []))
==> {apply map}
let f = \x acc -> [x] : (map ((:) x) acc)
in
f 1 (f 2 ([3] : []))
==> {list sugar}
let f = \x acc -> [x] : (map ((:) x) acc)
in
f 1 (f 2 [[3]])
==> {apply f}
let f = \x acc -> [x] : (map ((:) x) acc)
in
f 1 ([2] : map ((:) 2) [[3]])
==> {apply map}
let f = \x acc -> [x] : (map ((:) x) acc)
in
f 1 ([2] : [((:) 2) [3]])
==> {list sugar}
let f = \x acc -> [x] : (map ((:) x) acc)
in
f 1 [[2], [2,3]]
==> {apply f}
[1] : map ((:) 1) [[2], [2,3]]
==> {apply map}
[1] : [((:) 1) [2], ((:) 1) [2,3]]
==> {list sugar}
[1] : [[1,2], [1,2,3]]
==> {list sugar}
[[1], [1,2], [1,2,3]]
This is the general process can be used to understand the result obtained from evaluating expressions. Note that every step is a valid Haskell expression that behaves identically to the original expression. Essentially, I just expanded definitions, reduced case expressions when the case is matching on a (:) ... ... or a [], applied functions (using beta-reduction) and introduced some syntactic sugar for lists to make things a bit easier to read in parts. Those kinds of steps already cover a significant portion of the tools you need to reduce most Haskell expressions by hand.
A very similar process can also be used for equational reasoning, which can be used as a systematic technique to optimize Haskell programs. It works by replacing expressions with other expressions that always give the same result but could have different efficiency characteristics. Essentially anything written by Richard Bird will provide examples of equational reasoning, among others.

Defining append using foldr

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 (:))

How does fold works for empty list?

When we fold a list with one or more elements inside as done below:
foldr (+) 0 [1,2,3]
We get:
foldr (+) 0 (1 : 2 : 3 : [])
foldr (+) 1 + (2 +(3 + 0)) // 6
Now when the list is empty:
foldr (+) 0 []
Result: foldr (+) 0 ([])
Since (+) is binary operator, it needs two arguments to complete but here we end up (+) 0. How does it result in 0 and not throwing error of partially applied function.
Short answer: you get the initial value z.
If you give foldl or foldr an empty list, then it returns the initial value. foldr :: (a -> b -> b) -> b -> t a -> b works like:
foldr f z [x1, x2, ..., xn] == x1 `f` (x2 `f` ... (xn `f` z)...)
So since there are no x1, ..., xn the function is never applied, and z is returned.
We can also inspect the source code:
foldr :: (a -> b -> b) -> b -> [a] -> b
-- foldr _ z [] = z
-- foldr f z (x:xs) = f x (foldr f z xs)
{-# INLINE [0] foldr #-}
-- Inline only in the final stage, after the foldr/cons rule has had a chance
-- Also note that we inline it when it has *two* parameters, which are the
-- ones we are keen about specialising!
foldr k z = go
where
go [] = z
go (y:ys) = y `k` go ys
So if we give foldr an empty list, then go will immediately work on that empty list, and return z, the initial value.
A cleaner syntax (and a bit less efficient, as is written in the comment of the function) would thus be:
foldr :: (a -> b -> b) -> b -> [a] -> b
foldr _ z [] = z
foldr f z (x:xs) = f x (foldr f z xs)
Note that - depending on the implementation of f - it is possible to foldr on infinite lists: if at some point f only looks at the initial value, and then returns a value, then the recursive part can be dropped.

writing the prefix function (Haskell)

I'm writing a prefix function that will take a binary function and a list of numbers as parameters, and returns a list formed by computing the successive function and accumulating as you go.
To make things simple, Here's an example:
prefix (+) [2, 4, 1, 1]
returns [2, 6, 7, 8]
prefix (+) [0, 2, -3, 4, -5]
returns [0, 2, -1, 3, -2]
prefix max [2, 3, 1, 1]
returns [2, 3, 3, 3]
Here's my code so far however I get an error when i try to load the file because 'list is not in range'. How can i rewrite it so it makes sense to the compiler? Any help will be appreciated.
prefix' :: (a -> b) -> [a] ->[b]
prefix' _ [] = []
prefix' f (x:xs)
| ((list !! x) == 0) = f (list !! 0) (list !! 0)
| otherwise = prefix' f xs
Try this
prefix::(a -> a -> a) -> [a] -> [a]
prefix f lst| null lst = []
| null (tail lst) = lst
| otherwise = h : prefix' f (f h) (tail lst) where
h = head lst
prefix' fn fc (x:xs) | null xs = [acc]
| otherwise = acc : prefix' fn (fn acc) xs where
acc = fc x
I will try to explain the above code as much as possible. The type signature of the function is one that takes a function (a->a->a) and a list [a] as parameter and returns another list with that function applied to each adjacent pair of the list. The a in the parameter list simply implies any type (which can be anything). If we had specified a specific type (i.e. in Title case), the function will only work with that specific type
The function works by first checking if the list it recieved is empty (null lst), if so we simply return an empty list
The next thing it checks for is if the list only contains one item in it (null (tail lst)), in that case, we simply return the list
The third case is when we actually do something, and the first thing we do is to append the first element in the list to head of our new list (head lst) and call another function which we have defined on the fly to do compute the rest of the list (: prefix' f (f (head lst)) (tail lst)). Note the : separates the head from the rest of the list
The prefix' function has a type signature of (a -> a -> a) -> (a -> a) -> [a] -> [a] so as you can see the only thing different about it is that it takes one extra parameter which is a function (fc) that takes an element of type a and returns an element of type a. To create this function, we have simply passed one parameter to the initial function recieved as argument which creates this new function. This will be useful in computing the rest of the list
The base case for this new function is that if the list only contains one element, it applies the new parameter function fc to that element in the list and returns a list containing the return value of the function
Otherwise it will apply fc to the first element of the list and we generate fc again by applying fn to the return value of fc x.
If you are interested in learning how all these work, this is the website I've been using and my knowledge of haskell has improved greatly due to this website, so highly recommended
Isn't what you want to implement the scanl1 function? I'm a beginner too, but from what I understood, it goes like this:
scanl1 :: (a -> a -> a) -> [a] -> [a]
scanl1 f (x:xs) = scanl f x xs
scanl1 _ [] = []
The scanl function. which scanl1 uses, goes like this:
scanl :: (b -> a -> b) -> b -> [a] -> [b]
scanl = scanlGo
where
scanlGo :: (b -> a -> b) -> b -> [a] -> [b]
scanlGo f q ls = q : (case ls of
[] -> []
x:xs -> scanlGo f (f q x) xs)
Here's what hackage has to say about scanl:
scanl :: (b -> a -> b) -> b -> [a] -> [b] Source
scanl is similar to foldl, but returns a list of successive reduced values from the left:
scanl f z [x1, x2, ...] == [z, z `f` x1, (z `f` x1) `f` x2, ...]
Note that
last (scanl f z xs) == foldl f z xs.
So, I guess the flow of execution goes like this:
scanl1 (+) [2, 4, 1, 1]
scanl (+) 2 [4, 1, 1]
scanlGo (+) 2 [4, 1, 1]
2 : scanlGo (+) (+ 2 4) [1, 1]
2 : 6 : scanlGo (+) (+ 6 1] [1]
2 : 6 : 7 : scanlGo (+) (+ 7 1) []
2 : 6 : 7 : 8 : scanlGo []
2 : 6 : 7 : 8 : []
[2, 6, 7, 8]
The same thing happens with the (*) and the max functions that you mentioned. Hope this helps.

Haskell, Foldr, and foldl

I've been trying to wrap my head around foldr and foldl for quite some time, and I've decided the following question should settle it for me. Suppose you pass the following list [1,2,3] into the following four functions:
a = foldl (\xs y -> 10*xs -y) 0
b = foldl (\xs y -> y - 10 * xs) 0
c = foldr (\y xs -> y - 10 * xs) 0
d = foldr (\y xs -> 10 * xs -y) 0
The results will be -123, 83, 281, and -321 respectively.
Why is this the case? I know that when you pass [1,2,3,4] into a function defined as
f = foldl (xs x -> xs ++ [f x]) []
it gets expanded to ((([] ++ [1]) ++ [2]) ++ [3]) ++ [4]
In the same vein, What do the above functions a, b, c, and d get expanded to?
I think the two images on Haskell Wiki's fold page explain it quite nicely.
Since your operations are not commutative, the results of foldr and foldl will not be the same, whereas in a commutative operation they would:
Prelude> foldl1 (*) [1..3]
6
Prelude> foldr1 (*) [1..3]
6
Using scanl and scanr to get a list including the intermediate results is a good way to see what happens:
Prelude> scanl1 (*) [1..3]
[1,2,6]
Prelude> scanr1 (*) [1..3]
[6,6,3]
So in the first case we have (((1 * 1) * 2) * 3), whereas in the second case it's (1 * (2 * (1 * 3))).
foldr is a really simple function idea: get a function which combines two arguments, get a starting point, a list, and compute the result of calling the function on the list in that way.
Here's a nice little hint about how to imagine what happens during a foldr call:
foldr (+) 0 [1,2,3,4,5]
=> 1 + (2 + (3 + (4 + (5 + 0))))
We all know that [1,2,3,4,5] = 1:2:3:4:5:[]. All you need to do is replace [] with the starting point and : with whatever function we use. Of course, we can also reconstruct a list in the same way:
foldr (:) [] [1,2,3]
=> 1 : (2 : (3 : []))
We can get more of an understanding of what happens within the function if we look at the signature:
foldr :: (a -> b -> b) -> b -> [a] -> b
We see that the function first gets an element from the list, then the accumulator, and returns what the next accumulator will be. With this, we can write our own foldr function:
foldr :: (a -> b -> b) -> b -> [a] -> b
foldr f a [] = a
foldr f a (x:xs) = f x (foldr f a xs)
And there you are; you should have a better idea as to how foldr works, so you can apply that to your problems above.
The fold* functions can be seen as looping over the list passed to it, starting from either the end of the list (foldr), or the start of the list (foldl). For each of the elements it finds, it passes this element and the current value of the accumulator to what you have written as a lambda function. Whatever this function returns is used as the value of the accumulator in the next iteration.
Slightly changing your notation (acc instead of xs) to show a clearer meaning, for the first left fold
a = foldl (\acc y -> 10*acc - y) 0 [1, 2, 3]
= foldl (\acc y -> 10*acc - y) (0*1 - 1) [2, 3]
= foldl (\acc y -> 10*acc - y) -1 [2, 3]
= foldl (\acc y -> 10*acc - y) (10*(-1) - 2) [3]
= foldl (\acc y -> 10*acc - y) (-12) [3]
= foldl (\acc y -> 10*acc - y) (10*(-12) - 3) []
= foldl (\acc y -> 10*acc - y) (-123) []
= (-123)
And for your first right fold (note the accumulator takes a different position in the arguments to the lambda function)
c = foldr (\y acc -> y - 10*acc) 0 [1, 2, 3]
= foldr (\y acc -> y - 10*acc) (3 - 10*0) [1, 2]
= foldr (\y acc -> y - 10*acc) 3 [1, 2]
= foldr (\y acc -> y - 10*acc) (2 - 10*3) [1]
= foldr (\y acc -> y - 10*acc) (-28) [1]
= foldr (\y acc -> y - 10*acc) (1 - 10*(-28)) []
= foldr (\y acc -> y - 10*acc) 281 []
= 281

Resources