0

I swapped the order to two seemingly independent lines in a for-comprehension. The first gives expected result, while second throws exception. Why is this happening?

scala> for (a <- 1 to 3; b <- Some(2)) yield (a, b);
res6: scala.collection.immutable.IndexedSeq[(Int, Int)] = Vector((1,2), 
(2,2), (3,2))

scala> for (b <- Some(2); a <- 1 to 3) yield (a, b);
<console>:12: error: type mismatch;
found   : scala.collection.immutable.IndexedSeq[(Int, Int)]
required: Option[?]
      for (b <- Some(2); a <- 1 to 3) yield (a, b);
K.Chen
  • 1,166
  • 1
  • 11
  • 18

1 Answers1

2

If you check your compiled code, you see that for yield is desugered to flatMap equivalent to (1 to 3).flatMap(a => Some(2).map(b => (a, b))) which is valid.

scalac -Xprint:parser Test.scala
[[syntax trees at end of                    parser]] // Test.scala
package <empty> {
  object T extends scala.AnyRef {
    def <init>() = {
      super.<init>();
      ()
    };
    def main(args: Array[String]): Unit = {
      val x = 1.to(3).flatMap(((a) => Some(2).map(((b) => scala.Tuple2(a, b)))));
      println(x)
    }
  }
}

So, the inner computation is Some[Tuple2] wrapped in a outer List/ Vector context that's why you are able to flatMap to outer context as flatMap def is

final override def flatMap[B](f: Option[Int] => scala.collection.IterableOnce[B]): List[B]

While in second example,

Some(2).flatMap(b => List(1, 2, 3).map(a => (a, b)))

your are asking to .flatMap on Some context while the inner context being List/ or Vector. So it can not flatMap.

In summary what should work for flatMap is,

| Outer context | Inner context | Result    | example                  
|
|---------------|---------------|-----------|----------------------------------------
| List          | List[a]       | List[a]   | List(List(1, 2), List(2)).flatMap(a => a)
| List          | Option[a]     | List[a]   | List(Some(1), Some(2)).flatMap(a => a)
| Option        | Option[a]     | Option[a] | Some(1).flatMap(a => Some(a))
prayagupa
  • 30,204
  • 14
  • 155
  • 192