Make "fewerBraces" available outside snapshot releases

There’s now a PR that implements the proposed scheme. Change `fewerBraces` to always require a `:`. by odersky · Pull Request #15258 · lampepfl/dotty · GitHub.

While the changes to the existing syntax are small and manageable, the changes to the parser were extensive. The commits in the PR give some indication what had to be done. The most tricky part was about distinguishing tupled expressions and lambda parameters. This is always hard for an LL parser.
The particular trick we used before no longer works with the new rules.

To see why, consider the lambda (x: Int => Int) => Int. We used to parse (x: Int => Int) as an expression (i.e. an ascription with a Typed node) and then converted this to a parameter ValDef when the second => was encountered. Because of this hack, we had to allow function types like Int => Int in type ascriptions inside parentheses, but not elsewhere, even though this relaxation is not backed by the official Scala syntax. With fewerBraces the first expression inside (...) would be parsed as an application of the form x(y => z), which would complicate things further.

Instead of parsing as expression and converting later, we now employ lookahead. We scan forward when a : ascription inside parentheses is encountered until we hit the closing parenthesis. If that is followed by => or ?=> we classify the prefix as a list of bindings and parse appropriately. There’s no need anymore to convert type ascriptions to parameters. This means we can now align parser and official syntax and always require that a function type in an ascription is enclosed in parentheses.

However, this change does invalidate some existing code. I had to patch 9 community build projects, which used the loophole in some code: akka, cats, http4s, monocle, play-json, protoquill, scalatest, scalaz, and zio. There were between 1 and 4 changes per project that had to be made. The need for change is clearly communicated. In each case you see an error message like
this:

2 |val x1 = (f: Int => Int)  // error
  |             ^^^^^^^^^^
  |             function type in type ascription must be enclosed in parentheses

I did not touch by-nameness, since there are also use cases where we want to use : in a call-by-value setting, and I did not want to change syntax and semantics at the same time. : will expand to braces, and will behave the same.

6 Likes