Reputation: 6109
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.
Upvotes: 4
Views: 208
Reputation: 297205
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.
Upvotes: 7
Reputation: 1777
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]).
Upvotes: 1