I am having problems understanding this code from the Book FP in Scala. Here is the code:
trait Monoid[A] {
def op(a1: A, a2: A): A
def zero: A
}
def endoMonoid[A]: Monoid[A => A] = new Monoid[A => A] {
def op(f: A => A, g: A => A) = f compose g
val zero = (a: A) => a
}
def foldMap[A, B](as: List[A], m: Monoid[B])(f: A => B): B =
as.foldLeft(m.zero)((b, a) => m.op(b, f(a)))
// The function type `(A, B) => B`, when curried, is `A => (B => B)`.
// And of course, `B => B` is a monoid for any `B` (via function composition).
def foldRight[A, B](as: List[A])(z: B)(f: (A, B) => B): B =
foldMap(as, endoMonoid[B])(f.curried)(z)
foldMap
is expecting a function f: A => B
.
In foldRight
, when f
is curried you have A => (B => B)
, so I suppose f.curried
is working because it is the same as (A => B => B)
, so foldRight
is passing in to foldMap
what it expect (a function with type A => B
), then, what happends next is that foldMap
is called and its returning a function B => B
, and that's when z
comes into play in (f.curried)(z)
you call the function B => B
with the argument z
to get the final B
.
Am I right? it is a litle complicated to reason about this code for me.
NOTE: Here is a scalafiddle if you want to play with it.
Well, you seem to be mostly comprehensive to me. Nevertheless, I would clarify some points:
f.curried
is working because A => (B => B)
is the same as (A => B => B)
" (it is ambiguous here and you're talking about f.curried
result type basically, not with z
)foldMap
is expecting a function f: A => B . In foldRight, ... " and pretty much every where else. Shorter phrases, clearer explanation.(f.curried)(z)
doesn't work on its own and is not called after foldMap(as, endoMonoid[B])
. It's first foldMap(as, endoMonoid[B])(f.curried)
which is called and then (z)
. The first returns B => B
and called with the second returns B
.