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.