Почему для понимания расширяется до map + foreach вместо вложенного foreach? - PullRequest
0 голосов
/ 06 мая 2020

Я полагаю, что для понимания, которое я вижу как «для каждого 'a' создайте 'x', а затем для каждого 'b' сделайте что-нибудь со всеми варами»

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 =>
        ...
    }
}

Я думаю, что это нарушает естественное понимание того, что происходит в целом для понимания, потому что теперь сначала он определяет три разных «x», а затем выполняет другие вещи. Это может быть критичным, если определение 'x' может вызывать побочные эффекты, так какова цель удаления сахара до map?

1 Ответ

5 голосов
/ 06 мая 2020

Такое поведение удивительно, но оно задокументировано в спецификации Scala: https://scala-lang.org/files/archive/spec/2.13/06-expressions.html#for -comprehensions-and-for-loops

Согласно последнему правилу:

Генератор ? <- ?, за которым следует определение значения ? ′ = ? ′, транслируется в следующий генератор пар значений, где ? и ? ′ - fre sh имена: </p>

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

Итак, когда есть определение значения, Scala всегда вставляет новое понимание с yield, которое затем становится 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) => ...))))))
...