Question

I have two questions but I expect the answers are intertwined. So I was playing around with flatMapping different parametric types. I get the following:

val s: List[String] = List("f2", "df", "e")     //> s  : List[String] = List(f2, df, e)
val o = s.map(s => if (s.head == 'f')Some(s) else None)
                                              //> o  : List[Option[String]] = List(Some(f2), None, None)
val o1 = s.flatMap(s => if (s.head == 'f')Some(s) else None)
                                              //> o1  : List[String] = List(f2)
val a: Option[String] = Some("Hello")           //> a  : Option[String] = Some(Hello)
val a1 = a.map(s => s.toList)                   //> a1  : Option[List[Char]] = Some(List(H, e, l, l, o))

but

val a2 = a.flatMap(s => s.toList) //gives
//type mismatch;  found   : List[Char]  required: Option[?]

So I'm trying to understand the logic behind o1 compiling but not a2. And then looking into Option I was wondering why Option doesn't inherit from the traits: Seq and Set? Option is a Seq as it maintains order, and it is a Set because it contains no duplicates. Through Seq and Set it would inherit from Iterable and Traversable.

Was it helpful?

Solution

The assumption for all GenTraversableOnce descendants is that they contain an arbitrary number of elements. There's too much of the API and the mechanics that assumes or depends on that, such as Builder and CanBuildFrom.

On a deeper level, however, it is important to realize that for-comprehensions, and map/flatMap, are monadic operations. Monads are not interchangeable -- you cannot pick a a function A => N[B] and pass it to an M[A] to get an N[B], for any monad M and N, and Option and collections are different monads.

Through much implicit magic, all of the collections are treated as if they were a single monad, which leads people to assume that all monads should be interchangeable, which is simply not the case.

Then consider a simple case like this:

val x = Option(1)
val y = List('a', 'b', 'c')
val z = for {
  a <- x
  b <- y
} yield (a, b)

The type of z cannot be an Option, since the result has multiple elements. The only way for it to work would be for it to become something like an Iterable. That might make some sense for Option, if you think of it as a collection of at most one element, but it would not make sense for things like a State or Reader monad.

And speaking of thinking of Option as a collection of at most one, that is another reason not to do it. An Option should be thought of as the presence or absence of an element, not as a collection, and the methods available on which help this subtle distinction. Then again, I know of many people who think this argument, at least, is completely bogus, so take it with a grain of salt.

OTHER TIPS

While it is possible to map over an Option it is not a sequence nor a set (with only one element).

Flat map on option is defined like this

flatMap[B](f: (A) ⇒ Option[B]): Option[B]

So it wants a function that returns another Option. Your code

a.flatMap(s => s.toList)

does not return an Option[_] but a list of characters (s is a String and s.toList returns a List[Char]).

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top