Exercise 6.8, Chiusano and Bjarnason, Functional Programming in Scala, p. 87 asks how one might implement flatMap() for the following trait:
trait RNG {
def nextInt: (Int, RNG)
}
type Rand[+A] = RNG => (A, RNG)
The answer key gives the following solution:
def flatMap[A,B](f: Rand[A])(g: A => Rand[B]): Rand[B] =
rng => {
val (a, r1) = f(rng)
g(a)(r1) // We pass the new state along
}
Stackoverflow provides many answers to flatMap()/monad questions, but none which for me answered my questions regarding the next to last line of code.
I do not understand the syntax of the line
g(a)(r1)
(1) I do not understand how g(a)(r1) evaluates. What syntactic function is served by (r1)? The line does not exemplify currying, I do not believe, since g takes only one argument: A. (2) If g(a) will already return the type Rand[B], then why does not the line end here? (3) what is the relation between the Rand[B] returned by g(a) and the second set of parentheses: (r1)? (4) if the return type of this implementation of flatMap() is Rand[B], which is equal to RNG => (A, RNG), how are the enclosing parentheses to the right of the arrow generated? If I had to guess I would say they are generated by the evaluation, (r1), but I do not really understand this code given my questions 1 through 3.
Remember that we're calling f
and g
inside a new anonymous function, as indicated by the line
rng => { ... }
g
returns a Rand[B]
when given an A
, so g(a)
evaluates to a function from RNG
to (A, RNG)
.
So g(a)
returns a function expecting an RNG
as its argument, we can then call this with our r1
, which is of type RNG
.
The result of the call is (B, RNG)
. Now since the flatMap
signature expects you to return a Rand[B]
which is the same as RNG => (B, RNG)
and we return (B, RNG)
inside our function, it exactly matches the signature.