I have no objections to this style. I wouldn’t complain about having to use it.
Extra tooling support for what braces are ending would be cool, even for Scala 2. But more importantly, this style has to be encoded as a scalafmt config (and ideally an Intellij preset) to really be useful. It is far easier to use a style if I don’t have to think about it.
As far as official recommendations go, it might be easier to just call it something like the ‘Scala Center Style’ and let users decide if they want it (and IMO, if this style doesn’t at least get used by the compiler and Scala website examples, it is dead on arrival anyways. Someone has to be using a style to justify recommending it to others).
While this rule seems straightforward, it introduces a syntactic asymmetry in common constructs. Consider this valid Scala3 snippet:
if
println("if here")
condition()
then
println("then here")
bodyOne()
else
println("else here")
bodyTwo()
Under the proposed rule, the if and then clauses are apparently compliant because they are terminated by corresponding keywords. However, the else block is not. I’d argue this feels inconsistent – while an if condition rarely needs such “expressiveness”, the then and else blocks should be treated symmetrically. Similar concerns could be applied to match branches, for-yield/do and others.
One possible solution would be to restrict (and simplify) significant indentation even further: no line breaks allowed whatsoever. Maybe, this can be alleviated by inserting comments, if line grouping is still necessary:
if whatever
then
println("then here")
// Explain it
bodyOne()
else
println("else here")
// Complain about it
bodyTwo()
– that would keep the blocks visually connected.
I’d also like to quote this message, which I pretty much agree with:
If significant indentation were restricted to continuous lines only without breaks, then it would function as a natural extension to Scala2 syntax with shortcuts for certain straightforward, unambiguous cases.
In other words, braces would retain their primary function for defining blocks, whereas significant indentation could be used where braces are clearly redundant – like in Scala2 but with some additional “perks”. I believe that would look evolutionary rather than revolutionary.
I wrote 5+ years ago what would be needed to support significant whitespace in Emacs Scala 3 syntax support in "other" editors - #2 by fommil , a plan for anybody who wants to pick this up. That would automatically give all of these IDE features that you’re referring to. tree-sitter is not really taking off; major modern languages usually have an experimental alternative (and inferior) tree-sitter mode in emacs.
The only way you’ll get to “There should be” in all editors is to put Scala Center resources on implementing these things. It is not clear to me who you expect to do the work here. The tooling community is too small to pick it up and is anyway split across the Scala / Scala 3 divide for various reasons (many outside of our control).
Maybe when you say “IDEs“ you mean only IntelliJ and vscode. Which is also fine, but I then wonder who Scala 3 is intended for, if not all Scala developers.
Particularly like:
• documentation examples should have braces and significant indentation, as people need to be aware of both.
• no end markers: they are unusual enough, that barely anyone is used to them, and they don’t provide any of the benefits of braces (not syntactically distinct enough to make scoping visually clearer, don’t help with autoformatting)
I have a minor concern of finding a brace, followed by a dedent hard to read (as in the given example, though that is not even related to significant indentation), but I am not sure if this proposal would be the right place to address this.
Also, I do want to note that using indent.significant = 4 with scalafmt improved readability of significant indentation A LOT for me personally, making mixed style pretty readable overall. It means that a single dedent is not part of a block that was closed, which is helpful.
I’m in the braces camp and I don’t understand “end markers”, which is why I’m happy to see them deemphasized, with my wish to see them removed from the language, because TIMTOWTDI languages don’t go well.
Braces are more ergonomic and tooling doesn’t have to be smart. For instance, I can press [{ and ]} to go to the beginning or end of a lexical block marked by braces, or I can press % to find the matching brace, in any editor or IDE with Vim keyboard shortcuts (IntelliJ IDEA, VS Code, but also vim-minimal from any Docker container). And native English speakers may not be aware that all English keywords make learning harder for non-English speakers. If mathematics were this English-driven, children in non-English countries would have no math exposure until high-school. Thankfully, mathematics uses parens, square brackets and braces, instead of begin and end keywords.
Also, thanks to the C-family of languages (C++, Java, JavaScript/TypeScript, Dart, C#, Go, Rust, Obj-C, Perl, PHP, Swift), the vast majority of the code written uses braces. And some people may not like it, but Scala will always be in the C-family.
Also, Scala code using braces is here to stay, being the vast majority of code in the wild, which is what gives Scala legitimacy in the industry.
Scala’s significant indentation is probably here to stay, although I liked the Scala 2 syntax, because the rules are clear … i.e., you can avoid braces when you have expressions, and Scala being an expression-oriented language, you can actually avoid braces in a lot of instances.
But yes, I’d like to see some reconciliation, as in, using braces instead of “end markers”, and maybe some backtracking on the “fewer braces” features, because it gives CoffeeScript vibes.
Aesthetically, I can get on board with this proposal, but my experience with the braceless syntax is that mixing the two is really problematic both for the human and for the tooling. My uneducated guess is that the frontend compiler needs to deal with the possibility that even braced code might be braceless, and thus enforces indentation even for braced code. Usually the formatters can fix it, but sometimes you need to do some manual indentation first before things figure out what you mean.
My sense is that if we can get the tooling to “just work” with this, it would be fine, but maybe that’s easier said than done.
Also, as people have pointed out before, a lot of the “brace-free” stuff can be emulated in Scala 2 syntax simply by having discipline about keeping to single statements.
Regarding the LLM question, I’m sure we’re all using LLMS to generate and edit code more and more, but I am definitely still manually writing some code and absolutely am reading and verifying all the code that gets generated to make sure it is correct - so it’s important to settle on a style that aids both machine and human readability.
The first part in the doc for optional braces is about checking for reasonable indentation with braces, which I think is very useful. I intended to backport it for Scala 2, but the moment passed.
On “braceless in the small”, I think the new syntax shines for small edits (which might be lines of debug). An expression that is a brace-free one-liner needs braces just to add a println. (That is partly mitigated by using tap.)
My evolving opinion is that other weird new syntax with colon (especially with a lambda) at EOL also makes smaller expressions more legible (by reducing parens and braces, but also making formatting easier in that I don’t have to format enclosing parens).
As a last thought, I suspect there is not sufficient aesthetic rage against christmas trees of closing right braces.
Those are lovely! I wrote C++ (pre-11) in a style with closing braces on the same line as closing (so it looked a lot like braceless, except the braces were there).
The best part was the }}}} at the end of deeply nested blocks.
The worst part was that indentation guides weren’t really great back then, so it was kind of hard to figure out which depth I was at.
As an example of why this is so style-dependent, I’m working right now on code with 14 indentation levels (so far). Having 56 spaces before my code starts really would not help readability at all. (The nesting is class, function, match, future, try, while, if, match, future, map, if, if; matches eat two indents apiece, and the innermost expression captures values from the outermost match, and everything is one-shot so there’s no reason to refactor “for code reuse”. Note that the whole function is only 80 lines long–it’s kind of big, but not a gargantuan monstrosity. Note also that losing clarity on the match bounds, in exchange for “only” 48 spaces, is also not a win. If I used all-braces style instead of something very similar to the recommendation here, the function would be 114 lines long; also not a win.)
(My nestings got deeper once I started using Loom-style threads for futures.)
I don’t think its style dependent. For me, it’s either larger significant indents, or braces, or basically guessing on context clues where scopes end.
I am also not suggesting this to be a default, but maybe it’s helpful for someone like me, that kinda likes braceless aesthetically, but finds it lacking clarity. It makes a mixed style work for me. I’ll manage if I have to work with some code with less indent, the same I manage to work with other hard to read code, it’s fine.
Um, are you sure? You’re free to make your windows wider than 80 columns, of course, but I kind of doubt your style ever would lead you to write something that looks like this:
You might not be thinking about how your style impacts your choice of nesting, but how could you start writing that and not go, “Whoa, whoa, this is unusable. I can’t see anything. Let’s pull some of it out into a method.”?
Of course you should be free to style things how it suits you; I just don’t think the idea that the choice is without consequence is correct. It might be worth the tradeoff, or that might not even be a tradeoff for you because you’d never indent that deeply that anyway (for other reasons). But it might be a tradeoff for someone (e.g. me), and if it is for someone, that means that it is, to some extent, style-dependent whether it’s desirable.
(Also, if your editor can show really robust indentation guides, are you sure that is not clearer than more spaces? E.g. if you use VS Code, have you tried both the indent-rainbow extension and highlightActiveIndentGuide on?)
Using that many levels of indentation is a choice though. Usually, you can just extract those lambdas as functions with names. Scala certainly helps with the complexity due to its expression-oriented nature and its static typing, but Scala isn’t immune from cyclomatic complexity. And not making any judgements, some projects require it, which is also why some projects use 2-spaces of indentation in Python as well (I believe this was also Google’s guideline AFAIR), but the official recommendation has to target the common case, not the exceptions.
All languages with significant indentation should use at least 4-spaces as the recommendation, and people that don’t like it should be glad that the official recommendation isn’t 8-spaces
Yes, that’s why it’s a matter of style, not necessity.
Yes, but that makes code like this longer, harder to understand, more error-prone, and adds boilerplate as you add parameters that can easily be handled by closures.
It’s a choice, but it might be a well-founded choice.
Of course unless the Scala compiler stops accepting different indentations, people can use what they need to, when pressed. But the default recommendation should suit the common use-case, which is a balance between greater visual clarity between indentation depths and loss of screen space.
I’m happy enough for the recommendation to change, but only for well-founded reasons (which, when something is already established, includes the need to demonstrate that the alternative is clearly superior, not merely not-obviously-worse).
Firstly, there isn’t much science in computer science due to how expensive and error-prone the studies required get. This stuff is hard because we’re talking about the most effective ways for humans to communicate. And whatever arguments I can come up with, you may as well say that that’s not evidence, and it doesn’t apply to you. Therefore, requiring evidence to make a change isn’t warranted, and also, appealing to popularity is actually acceptable…
So, my best argument on this topic is this — here are the recommendations for languages with the off-side rule:
Make: tabs (8 characters);
Python: 4 spaces;
F#: 4 spaces;
Elm: 4 spaces;
Haskell: 2–4 spaces;
YAML: 2 spaces;
CoffeeScript: 2 spaces.
Well, I, for one, don’t see Scala being in good company here.
Here are some other C-family recommendations:
Java: 4 spaces
C: 4 spaces
Tabs / 8 spaces in the Linux kernel (that the standard for tabs is 8 spaces is specifically mentioned in the guideline)
C++: 4 spaces
Go: tabs
Rust: 4 spaces
C#: 4 spaces
PHP: 4 spaces
Kotlin: 4 spaces
Swift: 4 spaces
Obj-C: 4 spaces
JavaScript: 2 spaces
TypeScript: 2 spaces
Dart: 2 spaces
The reasoning given for why the Linux kernel uses tabs of 8 spaces is particularly noteworthy:
Rationale: The whole idea behind indentation is to clearly define where a block of control starts and ends. Especially when you’ve been looking at your screen for 20 straight hours, you’ll find it a lot easier to see how the indentation works if you have large indentations.
Now, some people will claim that having 8-character indentations makes the code move too far to the right, and makes it hard to read on a 80-character terminal screen. The answer to that is that if you need more than 3 levels of indentation, you’re screwed anyway, and should fix your program.
In short, 8-char indents make things easier to read, and have the added benefit of warning you when you’re nesting your functions too deep. Heed that warning.
Personally, I believe that the big difference here is that greybeards work on the Linux kernel, so they care about accessibility more than the youngsters working with JavaScript, maybe I’m wrong. But one does have to consider that using 2-spaces in a whitespace significant language, a language that’s very expression oriented, can be an accessibility issue for some people.
We talk about Python a lot, it’s at least in top 3 programming languages, but one has to consider that Python doesn’t allow multi-line expressions (without a \\) or multi-line lambdas to avoid ambiguity. Python, by design, is less ambiguous than Scala. And while Scala 3 brings plenty of safety via its expression-orientation and type system, right now Scala 3 is actually less like Python and more like CoffeeScript, a dead language.
We write code for other developers to read. The code is more read than written. Thus we have to focus for clarity and use the structure, which is easy to grasp for humans (computers don’t care much about how it looks, how easy it is parse).
And there must be the official style config, ideally it should be part of language, like in Elm.
And trailing commas (the trailingCommas = alwaysin scalafmt ling) is must have too.
Default style must be easy to grasp, support skimming of code and produce minimal diffs on changes.
It’s expected that people have different preferences; there are after all people who love Haskell, Coffeescript, and YAML! I can’t say their preferences are wrong, because it’s entirely subjective. Maybe lots of Scala folks are in the pro-Coffeescript camp, who knows.
But as a Scala language and community, do we want to be the “as readable as Haskell, Coffeescript, and YAML” language? Is ”Scala is a language that looks like Haskell, Coffeescript, and YAML” the impression that people get when they first look at our syntax?
There’s a lot of smart people in this thread with a lot of smart arguments, but fundamentally it comes down to this.
Are we making the right choice by throwing in our lot with the Haskells and YAMLs and Coffeescripts of the world, and the designers of more recent languages (e.g. Rust, Kotlin, Swift) are all making mistakes by not following suit?
Or are 2-space indents themselves a mistake that has broadly hampered the readability of the language, and more specifically crippled our attempt at indentation-based syntax, resulting in the widespread feedback that indentation-based syntax is “unreadable” because the indents are simply too small?
People seem fine with 4-space Python syntax. I don’t generally hear complaints about 4-space F# or Elm syntax, or 4-space Java/C#/Kotlin/Swift. Lots of people hate on 2-space YAML, and back in the day hated on 2-space Coffeescript. Now we are seeing people hating on indentation-based Scala syntax, could the 2-space indentation be part of the reason? Or even the reason?
Many years ago, when I was using Python for an undergraduate CS course project, I really liked it, more so than Java because it was aesthetically pleasing (e.g. less boilerplate, braces), readable, and resembled the pseudocode in my algorithms textbook.
However, I recall one particularly frustrating debugging session where my code kept producing incorrect results. I reviewed it over and over without finding the issue, only to eventually discover that a single line had the wrong indentation. As much as I personally prefer 2-space indentation, I have to concede that in certain situations it can genuinely hurt readability. (I scanned my code multiple times and did not see what was wrong…) In fairness to my past self, I was likely tired and staring at a small screen the night before a deadline.
That said, Scala is quite different from Python in that it offers much stronger compile-time safety and perhaps indentation mistakes are more likely to surface as compiler errors. So perhaps the readability concern is somewhat offset by that?
And perhaps with all the AI tools today maybe this might become less of an issue, but this past experience of “oh gosh… the issue with my code was due to indentation” in Python stuck with me.
Appealing to popularity is evidence as long as it’s not just, “Well, everyone thinks that…”. Language recommendations (and uses-in-the-wild) are absolutely evidence. It’s relevant, and what-it-is is objective. It might be an objective account of collective subjective opinion. One might hope for better evidence (e.g. usability research, not well-for-whatever-historical-reason-we-did-this-and-it-stuck). But of course it’s evidence! An accurate account of what people recommend (and do in practice) can be much better than opining alone.
So, this is exactly the kind of thing I had in mind! It’s very useful! (If accurate!)
You forgot nim, which is also two spaces.
How are you coming up with these numbers for recommendations?
I have worked on and looked at a lot of C and C++ codebases over the years. The C++ core guidelines says, “After many requests for guidance from users, we present a set of rules that you might use if you have no better ideas, but the real aim is consistency”. And there is a lot of 2-space-indent C++ out there. (gcc, blender, torch, openjdk, etc.)
The lesson from some of these languages is not “use N spaces” but “use the style that makes sense for your needs”, and a lot of massive projects have chosen two spaces as their consistent style, for good or ill, including languages that you labeled 4 spaces with no caveats. (The Swift standard library uses 2 space indents, incidentally. But XCode’s IDE likes 4-space indents by default.)
That certainly could be the case. The relevant comparison is to languages with similar challenges, and whitespace-significance is certainly the most striking similarity in challenge.
How much does 2- vs 4- space indentation help, though, as compared to braces-vs-not? If you look at extant code bases with huge numbers of contributors, it’s hard to conclude that a 2-space indent braced language is a common barrier.
And the most relevant comparison is hard to make: the significant indentation space just isn’t very heavily populated. You have the 800,000 pound gorilla, Python; and you have languages that just aren’t heavily used in both 4- and 2- space land (and both). When the key feature (2- vs 4-) doesn’t match the popularity gulf, how do we know how seriously to take the number of spaces vs. dynamic typing vs. awkward and limited lambda syntax? They all co-occur.
If nim/Coffeescript duke it out with F#/Elm, is the winner really all that clear (especially given that the differences in adoption are probably almost entirely due to other things)?
This is rather disingenuous. Haskell is weird because it has different syntactic conventions for how to represent things and uses a rather different conceptual model as well, not because it sometimes indents things two spaces. If I could understand Haskell easily and fluently if I simply indented it more that would be so awesome. Coffeescript had its best features adopted by JS, which I think is a tad more relevant than it having 2-space indentation (and also as a lesson for Scala, since Kotlin and Java have adopted some of Scala’s features), and consequently nobody even knows what Coffeescript looks like any longer. YAML isn’t even a programming language (it’s not Turing-complete), and the most striking thing about it is that you use "\n- " instead of commas as list separators. This is what would make Scala look (more) like YAML:
Anyway, I am not particularly opposed personally to having Scala 3 example code 4-indented, if it makes things clearer for people rather than being yet another change that takes people out of their comfort zone because they were already used to 2. We shouldn’t ignore the friction of a change, especially because it doesn’t fully respect the decisions of people who may have used the old way as part of their reason for adopting the language. It’s easier to irritate existing users than to appeal to new ones, because you have good access to existing users and not such great access to potential new users. But examples are disproportionately consumed by new users.
In examples, blocks tend to be very short, which makes a smaller indent easier to parse, and one is often limited horizontally so the value of preserving horizontal space is higher. But then again, in examples there are usually few indentation levels, so it’s less of an issue in any direction.
When it comes to indentation, though, I think it is especially important to consider how and whether tooling can make indentation clearer. For example, in my absurdly deeply indented example, isn’t it entirely the indentation guides that let you know how many levels it is, not eyeballing the massive gulf of 4-indent whitespace? I recognize that more sophisticated tooling is not always available (I don’t expect nano to include indentation guides, for instance). But we also shouldn’t optimize for the rare case if it makes the common case worse (or removes flexibility that is providing genuine benefit).
If we can lean a little more on crafting a good visual cue for indentation, the actual number of spaces matters less. The standard can be “have block structure visually apparent”, not a proxy like, “use 8 spaces”. Time is probably better spent on patches to syntax highlighters and editors than changing large fractions of code bases to different numbers of spaces, because what you gain can be greater clarity than any choice for number of spaces (for most people, at least), and it is almost all upside with very little tradeoff.
I strongly prefer to read and write code in my editor with 2 spaces and indentation guides
Because of, or as a consequence of, that my personal style tends to have a lot of different indentation levels.
But if again we see this Common Scala Style as something predominantly for docs and examples, I think it makes a lot of sense to go with 4 space indents.
We have to assume all the nice IDE tools are not available in those environments, and it seems people have trouble reading 2 space indents without braces (and probably indent guides).
So we should go with 4, simple as that.
(inb4 someone recommends we go with 3 instead)
Again, this common style is going further and further away from what I consider beautiful, practical, efficient, etc.
But it doesn’t matter, it doesn’t have to be those things, it has to read well on the worst possible setup you can imagine.
(We should maybe even try it on non-monospace, for the times people have their first bug, and don’t know how to escape code on forums)
P.S: Whatever we choose will probably be seen as the bible in enterprise software, and we should be conscious of that. But I still think documentation is more important than sub-optimal enterprise software