Search code examples
scalafor-comprehension

Scala for-comprehension yielding result from multiple variable bindings


I am very curious how Scala desugars the following for-comprehension:

for {
  a <- Option(5)
  b = a * 2
  c <- if (b == 10) Option(100) else None
} yield b + c

My difficulty comes from having both b and c in the yield, because they seem to be bound at different steps


Solution

  • You can even ask the compiler. The following command:

    scala -Xprint:parser -e "for {
      a <- Option(5)
      b = a * 2
      c <- if (b == 10) Option(100) else None
    } yield b + c"
    

    yields this output

    [[syntax trees at end of                    parser]] // scalacmd7617799112170074915.scala
    package <empty> {
      object Main extends scala.AnyRef {
        def <init>() = {
          super.<init>();
          ()
        };
        def main(args: Array[String]): scala.Unit = {
          final class $anon extends scala.AnyRef {
            def <init>() = {
              super.<init>();
              ()
            };
            Option(5).map(((a) => {
      val b = a.$times(2);
      scala.Tuple2(a, b)
    })).flatMap(((x$1) => x$1: @scala.unchecked match {
              case scala.Tuple2((a @ _), (b @ _)) => if (b.$eq$eq(10))
      Option(100)
    else
      None.map(((c) => b.$plus(c)))
            }))
          };
          new $anon()
        }
      }
    }
    

    Taking only the piece you are interested in and improving the readability, you get this:

    Option(5).map(a => {
      val b = a * 2
      (a, b)
    }).flatMap(_ match {
      case (_, b) => 
        if (b == 10)
          Option(100)
        else
          None.map(c => b + c)
    })
    

    Edit

    As reported in a comment, literally translating from the compiler output seems to highlight a bug in how the desugared expression is rendered. The sum should be mapped on the result of the if expression, rather then on the None in the else branch:

    Option(5).map(a => {
      val b = a * 2
      (a, b)
    }).flatMap(_ match {
      case (_, b) => 
        (if (b == 10) Option(100) else None).map(c => b + c)
    })
    

    It's probably worth it to ask the compiler team if this is a bug.