Home > Blockchain >  Scala - Map2 function on Option --> flatMap vs. Map vs. For-Comprehension
Scala - Map2 function on Option --> flatMap vs. Map vs. For-Comprehension

Time:03-15

Option is used for dealing with partiality in Scala, but we can also lift ordinary functions to the context of Options in order to handle errors. When implementing the function map2 I am curious on how to know when to use which functions. Consider the following implementation:

def map2[A,B,C] (ao: Option[A], bo: Option[B]) (f: (A,B) => C): Option[C] =
    ao flatMap {aa =>
      bo map {bb =>
        f(aa, bb)

aa is of type A, and bb is of type B which is then fed to F, giving us a C. However, if we do the following:

def map2_1[A,B,C] (ao: Option[A], bo: Option[B]) (f: (A,B) => C): Option[C] =
    ao flatMap {aa =>
      bo flatMap {bb =>
        f(aa, bb)

aa is still of type A, and bb is still of type B, yet we will have to wrap the last call in Some(f(aa, bb)) in order to get an Option[C] instead of a regular C. Why is this? What does it mean to flatten on BO here?

Last and not least, one could do the simpler:

def map2_2[A,B,C] (ao: Option[A], bo: Option[B]) (f: (A,B) => C): Option[C] = for {
    as <- ao 
    bs <- bo 
} yield(f(as,bs))

I know that for-comprehensions are syntactic sugar for ForEach'es, maps and flatmaps etc, but how do I, as a developer, know that the compiler will choose MAP with bs <- bo, and not flatMap?

I think I am on the verge of understanding the difference, yet nested flatmaps confuse me.

CodePudding user response:

Taking the last question first, the developer knows what the compiler will do with for because the behaviour is defined and predictable: All <- turn into flatMap except the last one which will be either map or foreach depending on whether or not there is a yield.

The broader question seems to be about the difference between map and flatMap. The difference should be clear from the signatures e.g. for List these are the (simplified) signatures:

def map[B]    (f: A => B)      : List[B]
def flatMap[B](f: A => List[B]): List[B]

So map just replaces the values in a List with new values by applying f to each element of type A to generate a B.

flatMap generates a new list by concatenating the results of calling f on each element of the original List. It is equivalent to map followed by flatten (hence the name).

Intuitively, map is a one-for-one replacement whereas flatMap allows each element in the original List to generate 0 or more new elements.

  • Related