Reputation: 6781
In the code below, version 1 gives the correct result. I make a small variation in V2. The None value had disappeared which is Ok as that is how For Expression works. But what is the reason the yield output in V2 no longer respects the data type returned by myList.lift() which is an Option (as in V1)?
val myList = List(12, 34, "ABC")
Version 1
for { i <- (0 to 3).toList } yield myList.lift(i)
// res1: List[Option[Any]] = List(Some(12), Some(34), Some(ABC), None)
Version 2
for {
i <- (0 to 3).toList
x <- myList.lift(i)
} yield x
// res2: List[Any] = List(12, 34, ABC)
Upvotes: 2
Views: 337
Reputation: 41209
Desugaring the first case:
// desugar for comprehension:
(0 to 3).toList.map(
i => myList.lift(i))
Desugaring the second case:
// desugar for comprehension:
(0 to 3).toList.flatMap(
i => myList.lift(i).map(
x => x))
// remove .map(x => x):
(0 to 3).toList.flatMap(
i => myList.lift(i))
// desugar flatMap:
(0 to 3).toList.map(
i => myList.lift(i)).flatten
The second case simplifies to the first case with a .flatten
at the end, which explains the difference in the results: res2 = res1.flatten
.
Scala can treat Option
as a sequence:
Some(foo) --> Seq(foo)
None --> Seq()
The .flatten
is just flattening the sequence of sequences.
If you're curious about the types:
scala.collection.Seq.flatten
requires that the 'inner' type have an implicit conversion to GenTraversableOnce[T]
Option[T]
to Iterable[T]
Iterable[T] <: GenTraversableOnce[T]
The <-
in x <- myList.lift(i)
doesn"t just assign a variable to a value, it "gets a value out of" myList.lift(i)
. When you "get a value out of" an Option[T]
, you get foo
for Some(foo)
and nothing for None
. "Getting nothing" means the yield
doesn"t run at all for a None
, so nothing shows up in the result for the "iteration" when i = 3
.
If you're curious about this "get a value out of" concept that is defined for Seq
, Option
, and many other types in Scala, it is defined for any Monad.
Upvotes: 7
Reputation: 466
If you de-sugar the for comprehensions
Version 1
List(0, 1, 2, 3).map({ i =>
myList.lift(i)
})
Version 2
List(0, 1, 2, 3).flatMap({ i =>
myList.lift(i).map({ x => x })
})
what is the reason the yield output in V2 no longer respects the data type returned by myList.lift()
The yield
does nothing to the output of List.lift
:
myList.lift(i)
returns Option[Any]
myList.lift(i).map({ x => x })
returns Option[Any]
It's the flatMap
that flattens the Option[Any]
to Any
(by discarding None
and un-wrapping Some(a: Any) => a
)
Upvotes: 3