# Help understanding map2

What should `map2` return in the case of `List[Int]`?

I’m trying for the 5th time to read the Monad chapter of the Red Book (Functional Programming in Scala by Chiusano and Bjarnason). Section 11.2 attempts to motivate monads by looking at previous implementations of `map2` for three classes introduced in previous chapters, `Gen`, `Parser`, and `Option`.
However, the generalization seems weird to me.

``````  def map2[A,B,C](fa:Gen[A], fb:Gen[B], f:(A,B)=>C):Gen[C] = {
fa flatMap (a => fb map (b => f(a,b)))
}
def map2[A,B,C](fa:Parser[A], fb:Parser[B], f:(A,B)=>C):Parser[C] = {
fa flatMap (a => fb map (b => f(a,b)))
}
def map2[A,B,C](fa:Option[A], fb:Option[B], f:(A,B)=>C):Option[C] = {
fa flatMap (a => fb map (b => f(a,b)))
}
``````

Thus in they define `map2` more generally as follows:

``````trait Mon[F[_]] {
def map2[A,B,C](fa: F[A], fb: F[B], f: (A,B)=>C): F[C] =
fa flatMap (a => fb map (b => f(a,b)))
``````

However, when I try to use this definition for List[Int] I get an inconsistent result.
Should `map2(List(1,2,3),List(10,20,30), _+_)` return `List(11,22,33)`, or should it return `List(11, 21, 31, 12, 22, 32, 13, 23, 33)` ? I was under the impression `map2` was supposed to apply the binary function to corresponding elements of the two lists, not every possible pairing.

Do I misunderstand `map2`?

`map2` as you defined it will indeed give you the cartesian product: `fb.map(f)` will apply `f` to all elements of `fb`, this is then applied to all elements of `fa` by your `fa.flatMap(g)`. For `List`s this is equivalent to the following imperative code:

``````val out = mutable.MutableList[C]()
for (i <- 0 until fa.length) {
for (j <- 0 until fb.length) {
out += f(fa(i), fb(j))
}
}
``````

If you want to apply it to the corresponding elements instead then you want to use zip, for example:

``````fa.zip(fb).map { case ((a, b)) => f(a, b) }
``````

This can also be shortened since we’re just applying it to `f` (`tupled` converts a function from taking N arguments to taking a single N-tuple argument):

``````fa.zip(fb).map(f.tupled)
``````

This is roughly equivalent to the following imperative code:

``````val out = mutable.MutableList[C]()
for (i <- 0 until math.min(fa.length, fb.length)) {
out += f(fa(i), fb(i))
}
``````
1 Like

The red book curiously avoids the discussion of the List monad in this section. The mysterious omission confuses the reader in my opinion. The implication is that map2 can be defined as shown, when actually it cannot.

In monad world `List` represents computations that can have many answers, and composing lists with `flatMap` (and `map2` as defined here) gives you every possible answer.

When you get to applicative functors you’ll see that `map2` characterizes something more general than monad, and indeed you can define a zipping `Applicative` instance for `List` as long as you forget that it’s a monad.

1 Like