Syntax rewriting Dotty options such as -indent -rewrite or -new-syntax -rewrite can be used to rewrite code from Scala 2 syntax to Scala 3 syntax and vice versa.
Scalafmt can achieve the same goal using the following options:
From my experience, Scalafmt is more robust and works better for this use case. In particular, it can apply both options in one run, while the compiler needs to be run twice, first with -indent -rewrite and then with -new-syntax -rewrite.
Therefore, I am wondering if it is really worth keeping and maintaining syntax rewriting in Dotty. What do you think? Are there situations in which using Scalafmt instead of the compiler options would not be feasible?
Scala 3 syntax rewriting from non-significant indentation (let’s call it curly braces based)/“old” control structures syntax to significant indentation/new control structures syntax can indeed be done in the two ways as you describe.
Rewriting in the other direction can only be done by the Scala 3 compiler as far as I know (but I may be missing something of course).
In addition to that, there is a bug in the compiler with the rewriting to curly braces syntax starting at version 3.0.2 and as reported in this issue.
In my opinion, a condition to remove the rewriting feature in scalac is that there should be an alternative that offers equivalent functionality.
Whatever the chosen path, fixing the bug in 3.0.0 would be very desirable.
I just stumbled upon the fact that the Scala 3 compiler can migrate code containing inconsistent parameters between method definitions and call sites (e.g. def next() called just next):
I believe such rewrite rules are impossible to implement in scalafmt, which is based on syntax only. I am not sure this is a big issue in practice, but I wanted to mention it in this discussion.
I actually think that we’d be throwing out a lot of potential if we do this. Arguably nothing should be able to provide as good of rewrites as the compiler seeing that the compiler simply knows the most about your code. We probably agree on this, but then it comes down to a maintenance / usage issue. I have no idea how many people are using the rewrite features of Dotty, but I do know that a ton of users are using scalafmt, which helps surface bugs and warts. Also scalafmt has a quite a bit of activity on the repo, often really quickly fixing the issues people report. Can the same be said about rewrite features?
One of the reasons I really hope the rewrites can be more rock solid and used is that I think we can also utilize them in other ways. For example in the discussion about actionable diagnostics the rewrite functionality is actually being re-used to create quickfixes that can be attached to the diagnostic. I hope to see a lot more of this in the future meaning that hopefully rewrites can get more focus, not less.
So the more I think about this the more I’m actually unsure. Yesterday I took some time and really wanted to try to do a full round trip with a somewhat minimal project that uses new syntax, including fewer braces. I was curious how smooth the process would go following what is written here.
Here’s a couple notes/questions I gathered trying out that process.
I wasn’t able to actual do the full round trip due to the rewrite functionality not working on some fewer braces syntax. So right off the bat this was sort of a bummer. We should probably have more robust tests for this.
Scalafmt was mentioned, but also keep in mind that looking at the Scala 3 rewrites that Scalafmt has can only go one way meaning you can migrate to new syntax, but not back.
Having to do the process in multiple steps currently is a big bummer. From a pure usability standpoint I feel like this should be catered to what most users would want, meaning migrate everything (syntax, control structures, etc) all at once. However it’s also a bit hard to know if that is what most users want since I don’t think we have a good idea about how widely used this feature is.
The rewrite functionality has been exercised much more from old syntax to new than the other direction. Arguably, old to new is the more important use case. I would be OK with keeping just this part and dropping the new to old direction.
Alternatively, if people find this important, we should find the resources to update rewriting. But I am not sure new to old is that important.
I think this is reasonable. It also aligns with what Scalafmt did, and I don’t see a ton of people (if any) request for Scalafmt to support the new → old rewrites. I’ll try to cast a wider net to see if anyone would actually mind this and then maybe we can just deprecate it, which would ease the maintenance burden as well.
The new to old rewrite is like a safety net: it makes it safer for the user to migrate to the new syntax, knowing that they can rollback any time later. Removing it would discourage some from using the new syntax: “I am happy with the old syntax, why would I take the risk of trying the new one”.
So, as long as there is no big technical difficulty, I would rather try to keep them both. Or to extract them both as Scalafix rules.
I wouldn’t worry much about that people feel safer having the option to revert back to the old syntax. In most cases, the decision to migrate is preceded by tests and discussions, and even if the developers change their minds, it usually happens shortly after, so they can simply git revert. The only case where the new → old syntax rewrite is useful, is when the change of the decision comes much later, but a) it should be pretty rare, b) sometimes the reasons for and against are in balance, so that the fact it’s difficult to revert is enough for the team to accept the change, and then they get used to it.
My personal opinion is that it’s not good to have two equal syntaxes in one programming language. If the new one is to be the future default, it makes sense to have only the old → new rewrite.
And about that if it should be in the compiler: I’d vote yes. Not everyone uses scalafmt. If it’s required to have scalafmt to do the rewrite, the migration will be slower.
I’m not sure that it’ll be rare enough case that it can be dismissed.
Style considerations tend to take a while to settle, and (if they’re not outright deal-breakers) are kind of like a rock in your shoe. A change that you can’t undo once everyone has had enough time to try it out and form an opinion is a pretty strong argument that it’s not worth the risk.
This is particularly true for something like fewer-braces, which is quite pretty for trivial examples and most people only see the warts once you hit a decently sized or complex project.
This feels kind of like setting a trap for users.
“Try it, the examples look pretty! You can always git revert if it doesn’t work for you*” * Revert expires after 3 merged features, after that: sucks to be you
I’m dismayed by the ageist rhetoric about “old” syntax. It’s just syntax. We were promised our preferred syntax would not be aged out.
In particular, I’d like to be able to submit a bug report to my favorite OSS project with a minimization that uses my preferred syntax, without worrying that it will be ignored by a maintainer with a different preference.
You know how cutting and pasting screws up indentation? Especially under WSL? I will probably use curlies when pasting.
We can leverage terminology from hairdressing: when someone wants curls, they get a “permanent”. I propose curly syntax be called “permanent” syntax.
The other syntax may be called “colonic”, another term from the spa.
This. There are a lot of us using Scala every day in serious industry projects who HateHateHate the Python-esque indentation-only style, and who shut up and stopped protesting the change only when Martin promised that brace-based would always be a valid Scala style.
Seriously: the mantra of the Scala 3 project was, “Don’t screw up like Python 3 and split the community.” Removing the braceful style will do that, and cause a bunch of people to reconsider ever migrating. Don’t do it.
Indeed, the primary reason I was bringing this up was in the hope to cut down some maintenance time. Especially since seeing how bad #17187 was—basically making -rewrite -no-indent unusable—gave me the impression that it wasn’t widely used it in the past year.
Yes, the technical difficulty of maintaining these rewrites is the most important question here. If it’s easy to maintain them, then there might be less appeal in removing them.
For me too. That’s the main reason I use scalafmt instead. The two passes needed by Dotty rewrites make it hard to set it up for automation, be it locally or on a CI.
To the persons who use the braces syntax, how often do you use -rewrite -no-indent and in what scenarios? Do you use it as a one-time action when migrating code bases? Would you consider using scalafmt or scalafix instead if they offered an option for the braces syntax?
Braceless syntax is personally so unusable enough for me that, if I were somehow stuck with a codebase that used the braceless syntax, I would have to run the conversion to braces after every git -pull, then back to braceless before committing, and back again before making the next change - so it would need to be damn near perfect.
This would only be an issue until I could find a new job, but it would still be a really annoying couple of weeks.
Jokes aside, why not start referring to the “curly braces” style syntax as “Classic” syntax?
Also, I think the copy paste scenario and the use of the rewrite to fewer braces syntax that you mention is an interesting one, and in itself a factor in justifying the retention of the rewrite functionality in the compiler.
Indeed; apart from old to new control structure syntax rewrite and “old syntax" to fewer braces syntax rewrites, the compiler can also rewrite non-symbolic infix notation (although the result looks a bit clunky as the function is wrapped in backticks), removal of symbol literals and procedure syntax.
On another topic; I think the current approach to testing the rewrite functionality with unit tests should be expanded to testing it on actual larger scale Scala code bases. The approach would consist of performing roundtrip conversions and verifying at each stage that the code (and tests) still compiles and the project’s tests pass. In fact, why not include this as part of the community build?
If this strategy would have been applied in the past, the bug would probably not have hit GA in the first place. A strong argument to keep rewrites in both directions.
Also, here’s an other consideration about the remark that the rewrite functionality is difficult to maintain; if you have better and systematic testing in place, problems are detected early and more easy to debug and fix. My 2 cents.