为什么 for-comprehension 扩展为 map+foreach 而不是嵌套的 foreach?

Why for-comprehension expands to map+foreach instead of nested foreach?

我想我认为的理解是 "for every 'a' create 'x' and then for every 'b' do some stuff with all of vars"

for {
    a <- Seq(1, 2, 3)
    x = "test" + a
    b <- Seq(4, 5, 6)
} {
    ...
}

应该扩展到

Seq(1, 2, 3).foreach { a =>
    val x = "test" + a
    Seq(4, 5, 6).foreach { b =>
        ...
    }
}

但令人惊讶地检查 -Xprint:parser 显示它扩展到

Seq(1, 2, 3).map { a =>
    val x = "test" + a
    (a, x)
}.foreach { case (a, x) =>
    Seq(4, 5, 6).foreach { b =>
        ...
    }
}

我认为它打破了对整个 for-comprehension 中发生的事情的自然理解,因为现在它首先定义三个不同的 'x' 然后执行其他东西。如果 'x' 的定义会产生副作用,那么对 map 进行脱糖的目的是什么?

这种行为令人惊讶,但它记录在 Scala 规范中:https://scala-lang.org/files/archive/spec/2.13/06-expressions.html#for-comprehensions-and-for-loops

根据最后一条规则:

A generator <- followed by a value definition ′ = ′ is translated to the following generator of pairs of values, where and ′ are fresh names:

(, ′) <- for (@ <- ) yield { val ′@′ = ′; (, ′) }

所以当有一个值定义时,Scala 总是插入一个新的带有 yield 的 for-comprehension,然后变成 map.

并且如果将值定义行 x = "test" + a 替换为生成器 x <- Seq("test" + a),结果将变为预期的结果:

Seq(1, 2, 3)
  .foreach(((a) => Seq("test".$plus(a))
    .foreach(((x) => Seq(4, 5, 6)
      .foreach(((b) => ...))))))