Hi,
I made the following trace to help myself:
If we have a list of functions such as:
f1, f2, f3, Nil
For easy reference, foldRight can be:
def foldRight[A,B](as: List[A], z: B)(f: (A, B) => B): B =
as match {
case Nil => z
case Cons(x, xs) => frf(x, foldRight(xs, z)(f)) // frf for foldRight function
}
Our function sequence uses foldRight this way:
So in our case frf is:
frf = (f, acc) => map2(f, acc)(_ :: _)
Because its foldRight we do the accumulation work from the right, which for Nil returns z:
z = rng => (Nil, rng) // what's returned by the unit function
Now foldRight is in:
case Cons(f3, Nil) => frf(f3, z)
frf is evaluated for f3 and z and returns what map2 returns, which is the function acc3:
acc3 = rng => {
(a3, r3) = f3(rng)
(Nil, r3) = z(r3) // returns z3 untouched
(a3::Nil, r3)
}
acc3 is what returned by the foldRight call we where in, so now we are in:
case Cons(f2, f3:Nil) => frf(f2, acc3)
frf returns now the function acc2:
acc2 = rng => {
(a2, r2) = f2(rng)
(a3::Nil, r3) = acc3(r2) // returns r3
(a2::a3::Nil, r3)
}
acc2 is what returned by the foldRight call we where in, so now we are in:
case Cons(f1, f2::f3:Nil) => frf(f1, acc2)
frf returns now the function acc1:
acc1 = rng => {
(a1, r1) = f1(rng)
(a2::a3::Nil, r3) = acc2(r1) // returns r3
(a1::a2::a3::Nil, r3)
}
So finally foldRight returns acc1.
Now if we apply acc1 on some rng we will see it calls acc2 and then combines its answer to a final answer. calling acc2 itself called acc3 which finally called z.
So foldRight transformed each function fX to another function accX and returned that function to the previous foldRight call, which captured a call to it in the body of the acc function it constructed, and so on.
And we got series of functions (correspond to the received functions list) which refer each to the next in its body by a call.