Description
I've been using Scala for years to write performance-critical code. One the pain points has always been the sub-par performance or seemingly benign for-comprehensions.
While it is clear that complex for-comprehensions emit inefficient code, a very common use-case is using them as a replacement for simple for-loops.
As an example, the two loops below do the same thing. The for-comprehension is cleaner (i is scoped more narrowly, no need for var). But one look at the decompiled code shows you just how much more inefficient the for-comprehension is.
Minimized code
class LoopTest {
final val count = 1000
def testWhile(): Unit = {
var i = 0
while (i < count) {
Console.println("Number: " + i)
i += 1
}
}
def testFor(): Unit = {
for (i <- 0 until count) {
Console.println("Number: " + i)
}
}
}
Output
I compiled using dotty-0.24.0-RC1. This is the decompiled output:
public void testWhile() {
for (int i = 0; i < 1000; ++i) {
Console$.MODULE$.println((Object)("Number: " + i));
}
}
public void testFor() {
RichInt$.MODULE$.until$extension(
Predef$.MODULE$.intWrapper(0), 1000).foreach(
(Function1)(i -> Console$.MODULE$.println((Object)("Number: " + i))));
}
Expectation
Is there some way to allow using for-comprehensions with a guarantee that performant code is being produced? Maybe it would be possible to introduce something like @scala.annotation.for (analogous to @scala.annotation.switch)?
For me, such an improvement would have a big impact: In my project, I spent hours translating for-comprehensions into simple while loops to reap (measurable) performance benefits. But it is unfortunate that in the process I had to make my code less readable and more error prone. Scala 2.13's inliner helped somewhat, but the results weren't reliable, so I mostly ended up avoiding for-comprehensions.