5

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.

Alejandro Alcalde
  • 5,990
  • 6
  • 39
  • 79

1 Answers1

2

Well, you seem to be mostly comprehensive to me. Nevertheless, I would clarify some points:

  • I'd rather say "so I suppose 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)
  • I'd rather put a point instead of a comma here: "foldMap is expecting a function f: A => B . In foldRight, ... " and pretty much every where else. Shorter phrases, clearer explanation.
  • what could be an error, (and what is confusing to you?) is that (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.
  • 1
    But I clearly don't know how to explain coarsely, yet, why `B => B` can be assimilated as a `B` by calling `foldMap` this way: `foldMap(as, endoMonoid[B])(f.curried)`. I guess it's because `A => (B => B)` is a supertype of `A => B` and that `B => B` is a subtype of `B`. If that's what is unclear to you, it'd be better to wait for someone who knows how to explain this point. – Benjamin Vialatou Apr 24 '19 at 13:09
  • 1
    Thank you for your answer. I have applied the edits. – Alejandro Alcalde Apr 24 '19 at 14:05