-
Notifications
You must be signed in to change notification settings - Fork 1.1k
Consider removing while
from the language
#5440
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Comments
It looks like it was added as a primitive for performance reasons. In "Scala By Example," Odersky includes essentially the same as your above code with this description:
|
It would be interesting to revisit the performance argument in case more recent optimisations render the difference irrelevant, in particular now that we have |
I tried playing with this a bit. It looks like the tail recursive implementation is about three times slower in my admittedly simplistic test. I imagine that given how common iteration is in lower level code this wouldn't make sense. Note that I wasn't able to use
|
The "Scala by example" book also mentioned "better error diagnostics." Maybe the extra lines in each stack trace is what he means?
|
@adamgfraser Re timing: what I would worry about most is short loops where the JIT doesn't get enough reps to deem it worth inlining. jmh would be useful here. It would also be interesting to compare the timings and the generated assembler using graal, which can be shockingly better on higher-order functions. |
Also: I mentioned on a twitter thread already, but it's interesting that the Dotty documentation contains an example with both
The following might be my personal quirks, but I tend to think
|
Remove it and let the compiler do optimization ? |
Adding |
What you need to do instead of macro def while(p: Expr[Boolean])(b: Expr[Unit]): Unit = '{
def w(): Unit = if (~p) { ~b; w() }
w()
} But it' s probably not worth doing. It's more interesting to automatically transform a mutating while loop into a pure tailrec method which takes all variables as arguments, but that's a lot more involved. |
@joroKr21 why would you need a macro ? I think you could do it as: inline def doWhile(p: => Bool): Unit = {
@tailrec def rec: Unit = if (p) rec
rec
} AFAIK the by-name parameter to the |
I was able to use
With that implementation and testing with JMH, the
I also checked the error reporting and we still get nice error messages. I'm not sure how much this all buys us because we're still using highly impure actions that have the type
|
Thanks for investing the time into benchmarking this, @adamgfraser - the results are certainly interesting! I also mentioned this to @odersky recently, and he suggested that the |
@propensive That totally makes sense. Is there anything else I can do to help? The results are encouraging but it is just one benchmark. Is there a broader set of benchmarks that we could use to determine whether there would be any reduction in performance? |
It seems nobody mentioned performance of the compiler. Compiling a while statement is probably much faster than inlining a def and optimizing it away. If this change made |
Maybe, especially since both closures have to involve outer references,
but we can test that too. Dotty has a suite of jmh tests for
compilation.
On Sun, Nov 18, 2018, at 12:58 PM, Lionel Parreaux wrote:
It seems nobody mentioned performance of *the compiler*. Compiling a
while statement is probably much faster than inlining a def and
optimizing it away.> If this change made while-heavy projects slower to compile (if there
are any?), then it'd be a bad idea.> — You are receiving this because you commented. Reply to this email
directly, view it on GitHub[1], or mute the thread[2].
|
The graal numbers are spectacularly better.
This is a macbook i5-8259U 2.3GHz with turbo shut off, fwiw.
Compiled from "Bench1.scala" public test.TestWhile(); public scala.collection.immutable.IndexedSeq<java.lang.Object> as(); public final void While0(scala.Function0, scala.Function0); public int nativeSum(); public int inlineSum(); public int tailRecSum(); private final void go$1(scala.runtime.IntRef, scala.runtime.IntRef); private final boolean tailRecSum$$anonfun$1(scala.runtime.IntRef); private final void tailRecSum$$anonfun$2(scala.runtime.IntRef, scala.runtime.IntRef);
|
It's actually the other way round. The body of @tailrec methods gets translated to |
There's still a bit of a mystery about why One thing I did notice is that in all cases we seem to invoke |
I just wanted to say "thanks" to everyone who's getting involved with this issue: for a somewhat frivolous issue, it's exactly the sort of discussion I hoped to see around it, and the sort of contributions I'm not knowledgeable enough to make myself! |
Closing for now since no immediate action is planned. I agree it was a good discussion to have! |
It seems that
while
does not need to be a part of the core language. A simple definition such as,in
Predef
would suffice to provide the same functionality, and would enable a slight reduction in the size of the core language. I don't know what difference (if any) it would make to performance characteristics. There would be a potential disadvantage that it would become possible to shadow thewhile
method, but not by existing code, of course.Something similar would probably be possible with
do
/while
syntax.The text was updated successfully, but these errors were encountered: