37

While going through Functional Programming in Scala, I came across this question:

Can you right foldLeft in terms of foldRight? How about the other way around?

In solution provided by the authors they have provided an implementation as follows:

def foldRightViaFoldLeft_1[A,B](l: List[A], z: B)(f: (A,B) => B): B = 
    foldLeft(l, (b:B) => b)((g,a) => b => g(f(a,b)))(z)

  def foldLeftViaFoldRight[A,B](l: List[A], z: B)(f: (B,A) => B): B = 
    foldRight(l, (b:B) => b)((a,g) => b => g(f(b,a)))(z)

Can somebody help me trace through this solution and make me understand how this actually gets the foldl implemented in terms of foldr and vice-versa?

Thanks

sc_ray
  • 7,803
  • 11
  • 63
  • 100
  • 2
    This is [an exercise](http://book.realworldhaskell.org/read/functional-programming.html#Fold.hs:myFoldl) from the RWH (Real World Haskell) book. There are some very helpful comments inside the yellow block, titled "Understanding foldl in terms of foldr". – George Jun 17 '13 at 15:35
  • 2
    Indeed Haskell's definition beats Scala on WTFs/s: ```myFoldl f z xs = foldr step id xs z where step x g a = g (f a x)``` – Hugo Sereno Ferreira Sep 21 '13 at 14:18
  • Is there a resource with the solutions for the exercises? – piotr Aug 18 '15 at 16:26

5 Answers5

34

Let's have a look at

def foldLeftViaFoldRight[A,B](l: List[A], z: B)(f: (B,A) => B): B = 
  foldRight(l, (b:B) => b)((a,g) => b => g(f(b,a)))(z)

(the other fold is similar). The trick is that during the right fold operation, we don't build the final value of type B. Instead, we build a function from B to B. The fold step takes a value of type a: A and a function g: B => B and produces a new function (b => g(f(b,a))): B => B. This function can be expressed as a composition of g with f(_, a):

  l.foldRight(identity _)((a,g) => g compose (b => f(b,a)))(z);

We can view the process as follows: For each element a of l we take the partial application b => f(b,a), which is a function B => B. Then, we compose all these functions in such a way that the function corresponding to the rightmost element (with which we start the traversal) is at far left in the composition chain. Finally, we apply the big composed function on z. This results in a sequence of operations that starts with the leftmost element (which is at far right in the composition chain) and finishes with the right most one.

Update: As an example, let's examine how this definition works on a two-element list. First, we'll rewrite the function as

def foldLeftViaFoldRight[A,B](l: List[A], z: B)
                             (f: (B,A) => B): B =
{
  def h(a: A, g: B => B): (B => B) =
    g compose ((x: B) => f(x,a));
  l.foldRight(identity[B] _)(h _)(z);
}

Now let's compute what happens when we pass it List(1,2):

List(1,2).foldRight(identity[B] _)(h _)
  = // by the definition of the right fold
h(1, h(2, identity([B])))
  = // expand the inner `h`
h(1, identity[B] compose ((x: B) => f(x, 2)))
  =
h(1, ((x: B) => f(x, 2)))
  = // expand the other `h`
((x: B) => f(x, 2)) compose ((x: B) => f(x, 1))
  = // by the definition of function composition
(y: B) => f(f(y, 1), 2)

Applying this function to z yields

f(f(z, 1), 2)

as required.

Petr
  • 62,528
  • 13
  • 153
  • 317
  • 2
    Pudlak - Thanks. I understand this is a way of deferring the execution of the function until it gets to the very last element of the List but I am still having a hard time trying to envision the trace of the execution. – sc_ray Jun 16 '13 at 22:44
  • I haven't totally grokked it yet but I guess I will eventually get the gist while mulling over your answer. Thanks for your response. – sc_ray Jun 18 '13 at 07:08
  • 2
    @sc_ray I updated the answer with an example showing how the expressions are evaluated. – Petr Jun 18 '13 at 07:44
  • I don't understand why (a,g) => b => g(f(b,a)) can be rewritten as (a,g) => g compose (b => f(b,a))? In the first case, the b parameter is at the far left, whereas in the second case, it has moved inside the parameter given to the function g. Can you clarify why this is valid? – Hedley Oct 20 '15 at 11:42
  • 1
    I'm a bit late to the party. The author's noted that it was a hard question. But since they asked, they must have thought it was possible for someone following along to find the solution. Should I feel bad if I didn't get to it myself :-)? – chosenbreed37 Aug 26 '16 at 12:33
  • @Hedley if you cancel the `(a,g) =>` and `f(b,a)` parts from both expressions you get `g compose (b => x)` and `b => g(x)`. Think about the definition of `compose` - these expressions are functionally equivalent – Alex Jan 11 '17 at 11:43
14

I just did this exercise, and would like to share how I arrived at an answer (basically the same as what's in the question, only the letters are different), in the hope that it may be useful to someone.

As background, let's start with what foldLeft and foldRight do. For example, the result of foldLeft on the list [1, 2, 3] with the operation * and starting value z is the value ((z * 1) * 2) * 3

We can think of foldLeft as consuming values of the list incrementally, left to right. In other words, we initially start with the value z (which is what the result would be if the list were empty), then we reveal to foldLeft that our list starts with 1 and the value becomes z * 1, then foldLeft sees our list next has 2 and the value becomes (z * 1) * 2, and finally, after acting on 3, it becomes the value ((z * 1) * 2) * 3.

                             1    2    3
Initially:               z
After consuming 1:      (z * 1)
After consuming 2:     ((z * 1) * 2
After consuming 3:    (((z * 1) * 2) * 3

This final value is the value we want to achieve, except (as the exercise asks us) using foldRight instead. Now note that, just as foldLeft consumes values of the list left to right, foldRight consumes values of the list right to left. So on the list [1, 2, 3],

  • This foldRight will act on 3 and [something], giving a [result]
  • Then it will act on 2 and the [result], giving [result2]
  • Finally it will act on 1 and [result2] giving the final expression
  • We want our final expression to be (((z * 1) * 2) * 3

In other words: using foldRight, we first arrive at what the result would be if the list were empty, then the result if the list contained only [3], then the result if the list were [2, 3], and finally the result for the list being [1, 2, 3].

That is, these are the values we would like to arrive at, using foldRight:

                             1    2    3
Initially:                             z
After consuming 3:                 z * 3
After consuming 2:           (z * 2) * 3
After consuming 1:     ((z * 1) * 2) * 3

So we need to go from z to (z * 3) to (z * 2) * 3 to ((z * 1) * 2) * 3.

As values, we cannot do this: there's no natural way to go from the value (z * 3) to the value (z * 2) * 3, for an arbitrary operation *. (There is for multiplication as it's commutative and associative, but we're only using * to stand for an arbitrary operation.)

But as functions we may be able to do this! We need to have a function with a "placeholder" or "hole": something that will take z and put it in the proper place.

  • E.g. after the first step (after acting on 3) we have the placeholder function z => (z * 3). Or rather, as a function must take arbitrary values and we've been using z for a specific value, let's write this as t => (t * 3). (This function applied on input z gives the value (z * 3).)
  • After the second step (after acting on 2 and the result) we have the placeholder function t => (t * 2) * 3 maybe?

Can we go from the first placeholder function to the next? Let

      f1(t) = t * 3
and   f2(t) = (t * 2) * 3

What is f2 in terms of f1?

f2(t) = f1(t * 2)

Yes we can! So the function we want takes 2 and f1 and gives f2. Let's call this g. We have g(2, f1) = f2 where f2(t) = f1(t * 2) or in other words

g(2, f1) = 
    t => f1(t * 2)

Let's see if this would work if we carried it forward: the next step would be g(1, f2) = (t => f2(t * 1)) and the RHS is same as t => f1((t * 1) * 2)) or t => (((t * 1) * 2) * 3).

Looks like it works! And finally we apply z to this result.

What should the initial step be? We apply g on 3 and f0 to get f1, where f1(t) = t * 3 as defined above but also f1(t) = f0(t * 3) from the definition of g. So looks like we need f0 to be the identity function.


Let's start afresh.

Our foldLeft(List(1, 2, 3), z)(*) is ((z * 1) * 2) * 3
Types here: List(1, 2, 3) is type List[A]
             z is of type B
             * is of type (B, A) -> B
             Result is of type B
We want to express that in terms of foldRight
As above:
 f0 = identity. f0(t) = t.
 f1 = g(3, f0). So f1(t) = f0(t * 3) = t * 3
 f2 = g(2, f1). So f2(t) = f1(t * 2) = (t * 2) * 3
 f3 = g(1, f2). So f3(t) = f2(t * 1) = ((t * 1) * 2) * 3

And finally we apply f3 on z and get the expression we want. Everything works out. So

f3 = g(1, g(2, g(3, f0)))

which means f3 = foldRight(xs, f0)(g)

Let's define g, this time instead of x * y using an arbitrary function s(x, y):

  • first arg to g is of type A
  • second arg to g is of the type of these f's, which is B => B
  • So type of g is (A, (B=>B)) => (B=>B)
  • So g is:

    def g(a: A, f: B=>B): B=>B = 
        (t: B) => f(s(t, a))
    

Putting all this together

def foldLeft[A, B](xs: List[A], z: B)(s: (B, A) => B): B = {
    val f0 = (b: B) => b

    def g(a: A, f: B=>B): B=>B =
        t => f(s(t, a))

    foldRight(xs, f0)(g)(z)
}

At this level of working through the book, I actually prefer this form as it's more explicit and easier to understand. But to get closer to the form of the solution, we can inline the definitions of f0 and g (we no longer need to declare the type of g as it's input to foldRight and the compiler infers it), giving:

def foldLeft[A, B](xs: List[A], z: B)(s: (B, A) => B): B =
  foldRight(xs, (b: B) => b)((a, f) => t => f(s(t, a)))(z)

which is exactly what is in the question, just with different symbols. Similarly for foldRight in terms of foldLeft.

ShreevatsaR
  • 38,402
  • 17
  • 102
  • 126
  • 1
    wooow! You are great! :) i was trying to understand this for a very long time and I think this is the proper explanation. Thanks!! – N. Sola Oct 23 '19 at 17:45
7

That code is chaining several function objects together, one function for each element in the list. Here is an example that shows that more clearly.

val f = (a: Int, b: Int) => a+b
val list = List(2,3,4)
println(list.foldLeft(1)(f))

val f1 = (b: Int) => f(b, 2)
val f2 = (b: Int) => f(b, 3)
val f3 = (b: Int) => f(b, 4)
val f4 = (b: Int) => b

val ftotal = f1 andThen f2 andThen f3 andThen f4
println(ftotal(1))

You can imagine that as a linked list of function objects. When you pass in a value it "flows" through all the functions. It's a little like dataflow programming.

Tesseract
  • 8,049
  • 2
  • 20
  • 37
1

Authors of the book provide a good explanation on their github/fpinscala page.

Tomato
  • 772
  • 8
  • 17
0

I think there is an error in @ShreevatsaR answer. It says foldLeft after acting on the list [1, 2, 3] with the operation * and starting value z in the list would be (((z * 1) * 2) * 3, forgetting to close the bracket. It then says foldRight would be ((z * 1) * 2) * 3, which is the same statement as the previous one except without the unpaired parenthesis.

foldLeft and foldRight are not the same. foldLeft would be (((z * 1) * 2) * 3) as correctly stated in the answer except for the unpaired parenthesis is now paired. And foldRight should be (1 * (2 * (3 * z))). I don't have enough reputation to make a comment which is why I made this answer so that someone can who does can add the comment before this gets removed.

numq
  • 1
  • 1