Scala 3 significant indentation

I am sure that for every person that feels this way, there’s a person that feels the exact opposite.

6 Likes

Indeed. And also let’s all please keep in mind that most people here aren’t noobs with no programming experience. Even though I’m sure it’s not anyone’s intention, it can come across as dismissive and even a bit condescending to keep throwing around the “I’m telling you it’s good and you’ll learn love it once you give it a chance / get used to it” response. I presume that most programmers have worked in other languages before and a decent subset will already have experience in indentation-based languages. It’s unfair to always attribute their objections to inexperience.

I’m an example. I’ve experience in both styles and I’ve come to think that braces are better than whitespace. I’m not super passionate about it but overall I find there are no real advantages to one other the other, but, subtle and significant disadvantages to whitespace.

10 Likes

Btw can we clarify what the plan is? I was previously under the impression that one style would be chosen for Scala 3 and be the only style supported from 3.0 onwards. Then reading the above it sounds like both styles may be available simultaneously and then in the far future, one dropped. It would be really good to clarify what the story is.

2 Likes

Well, this was said on the gitter channel

So brace are going to stay no matter what (?) yielding even more styles of writing code.

3 Likes

Although I sympathize with the sentiment that there is not a lot of transparency on this issue, Martin Odersky is moving mountains to nail down “asless givens” very late in the development cycle. I think the claim he isn’t listening is unfair. Indeed, one may say that Martin must go to the mountain.

I would say it’s the process equivalent of an implementation restriction.

Let’s stipulate that the feature is “optional braces.”

As already hinted, it’s the same as if you felt strongly about using semi-colons. For example, the following semi is optional:

for (x <- xs ; if x > 0) yield x + 1

as is this one:

v match { case p() => x ; case q() => y }

and this entirely apart from the line-ending semi, which may or may not be optional:

def f: Int = 42;
  + 17

as attested

➜  ~ scala
Welcome to Scala 2.13.4 (OpenJDK 64-Bit Server VM, Java 11.0.9.1).
Type in expressions for evaluation. Or try :help.

scala> {
     | def f: Int = 42
     |   + 17
     | f
     | }
         + 17
         ^
On line 3: warning: Line starts with an operator that in future
       will be taken as an infix expression continued from the previous line.
       To force the previous interpretation as a separate statement,
       add an explicit `;`, add an empty line, or remove spaces after the operator.
         + 17
         ^
On line 3: warning: a pure expression does nothing in statement position; multiline expressions might require enclosing parentheses
val res0: Int = 42

scala>
:quit
➜  ~ scala -Xsource:3
Welcome to Scala 2.13.4 (OpenJDK 64-Bit Server VM, Java 11.0.9.1).
Type in expressions for evaluation. Or try :help.

scala> {
     | def f: Int = 42
     |   + 17
     | f
     | }
val res0: Int = 59

scala>

Needless to add,

➜  ~ ~/scala3-3.0.0-M1/bin/scala
Starting scala3 REPL...
scala> {
     | def f: Int = 42
     |   + 17
     | f
     | }
def f: Int
val res0: Int = 59

scala>  

Let’s also stipulate that if you’re not doing K&R braces, you’re not really doing braces anyway.

braces and parens are interchangeable

Let’s agree that this has no syntactic basis.

For example, to explain f(x) and f { x }, you might say that f(x, y) is the anomaly. If a function just takes an arg, it doesn’t matter if the arg looks like an expression or a block. (Apologies to the educators.)

Another optional chestnut is extends:

scala> object X extends { def x = 42 }
object X

scala> X.x
val res0: Int = 42

I would suggest that the uproar over optionality has much in common with differences over the formatting of comments. Scala acknowledges two or three formats, of which all but one are misguided and wrong. Such is the state of political discourse in America. But I think the important thing is that people can make headway in whatever it is they undertake.

3 Likes

No. The indentation syntax has always been called “optional braces” (by analogy with scala’s existing optional semicolons support): https://dotty.epfl.ch/docs/reference/other-new-features/indentation.html

I think the name “optional braces” is a bit misleading, because it might be read as making all braces optional. But actually, the proposal only makes some braces optional, while other braces are still required.

I’d be willing to endorse the rubric “some optional braces”, if we also advertised “some optional semi-colons”, because not all semis are optional. Depending, as some wise person must have said, on context.

2 Likes

Oh ok thanks, I guess I missed that. In that case what’s the long-term plan or motivation for this experiment? Is it that if i works out well and is favoured for many years, that braces might go away in 4.0 or something? Is the long-term vision whitespace default, braces optional like it will in 3.0+ and then one day the Scala stops calling it experimental? Is it literally just an experiment to see where it leads, no long-term vision in mind? Maybe it’s just me but I’d appreciate a bit more context to understand this better.

4 Likes

Yes, maybe “Sometimes optional semicolons” is more accurate.

On the other hand, it is different in practical terms. Your example with the +17 is a bit strained, because the +17 serves no purpose.

In my code, I’m not sure if I have even a single semicolon, but there are plenty of places that cannot be written without braces, for example I have methods that look like this:

def m(f: A => B)(g: C => D): R = ...

and then are typically called like this:

val result = m { a =>
...
}{ c =>
...
}

and as far as I know, the above call cannot be written without braces.

I guess they can do the recursive Fibonacci numbers in CS 101 without braces, but I don’t think that should be the standard.

3 Likes

Braces would never go away, just like they never went away in Haskell and just like semicolons never went away in Scala. If the syntax is released as part of Scala 3 it won’t be experimental, it will be the default recommended way to do things.

1 Like

As the original proponent of a Scala whitespace-delimited syntax (https://github.com/lihaoyi/Scalite, circa 2014), I agree with @curoli that the standard that needs to be reached is “zero required braces”.

To me, “sometimes optional braces” to me is significantly worse than “never optional braces”. Imagine if Scala code had a mix of required and non-required semi-colons, or if Python had a mix of indentation syntax and require curly braces. Either would be awful.

I don’t even think it would be that hard to get to “zero required braces”. For example, my original Scalite prototype handles this as such:

val result = m do a =>
...
do c =>
...
}

Where the do keyword is a stand-in to open a block in all scenarios, which is closed on a decrease in indentation. You can look at the linked Scalite repo above to see many examples of it working.

Hang-ups over an arbitrary specific english meaning of do aside (that anyway doesn’t really apply to programming languages, e.g. haskell or coffeescript), having a specific, unambiguous indentation-block-start delimiter has a very large number of advantages. As a bonus do is currently an almost-unused keyword, and any existing usages of do{...}while(...) can be mechanically substituted with while({...; ...})(); (it looks even prettier with indentation syntax, as Scalite shows).

do could also be in addition to :, rather than replacing it. If we’re ok with having multiple keywords open indentation-based blocks in difference scenarios, adding another one to elegantly handle the multi-line lambda scenario doesn’t seem like a big deal.

4 Likes

With -Yindent-colons you can write this as

val result = m:
  a =>
    ...
:
  c =>
    ...

As long as there is an option to turn off this feature and maintain good ol’ bruce syntax, I don’t see a problem here. Well, as long as there’s a no-brainer way/tool to convert from one syntax to another. Copy-pasting code is still very useful.

Speaking of indentation, scalafmt and codebase migrations, it’s just so happen that at my current place of employment, we wish to move to scalafamt. Alas, most of our code has a tab size of 4 spaces, and scalafmt doesn’t support anything other than 2 spaces. This is intentional “as it is recommended in official Scala guide”.

I would hate it if we end up with a significant-indentation oriented tooling and ecosystem without enough support to maintain the current syntax.

3 Likes

Just my two cents. I tried optional braces syntax, and to my suprise, I really like it. I think it’s due to the combination of:

  • higher “signal-to-noise” ratio
  • saves a line with single }, allowing for more compact vertical formatting
  • { and } are a bit clunky to type (on a Norwegian keyboard)

Isn’t this exactly the same for optional semicolons? (There are places where you need them, unless you change the layout of the code.) Yet, we still call them optional semicolons.

2 Likes

As long as there is an option to turn off this feature and maintain good ol’ bruce syntax, I don’t see a problem here.

I do : introducing new people to scala.

lets start with the basics: scope, so a scope is defined by indentation, unless the project you are working on has disabled it so make sure to check the build config. Oh and of course as you navigate through the source code of libraries which the project use you might encounter brace using libraries in particular in scala 2 which doesn’t support significant whitespace notation. Also the current convention is to use braces here and here and here but not use braces here and here and oh yeah there is this : variant you may encounter once in a while but that’s not really used much outside of this specific part of the library ecosystem so you will be just fine.

And I can imagine project switching from one model to the other as a maintainer change leaving half the code using braces and the other half using significant whitespace.

Isn’t this exactly the same for optional semicolons?

Semicolon usage in existing codebases is almost zero except for extreme conciseness and I have yet to encounter someone actively defending using semicolons everywhere.

My personnal preference is for braces but I woud still take brace-less significant whitespace over a configurable mix of both without thinking twice.

3 Likes

Great, there is a possibility to write such code without braces. But at what cost?

  • Scala 3 now has at least three different syntaxes to be picked from via compiler options? That’s absolutely horrible.

  • It also horrifies me that all those type ascriptions where the type goes to a new line look like blocks now.

  • And note that the braceless version of my example actually has more lines and a line with a lonely colon. I thought the argument against brace syntax was that we want to save lines by avoiding lines with lonely close-braces?

So, we are looking for the best block start marker. Should it be colon? Or do? Come on, there is one block start marker that every programmer immediately understands, and it is free and unambiguous: open-brace. {.

That close-braces by convention take up a whole line on their own bothers me, too. But that could be solved by simply changing the convention.

7 Likes

Don’t forget <code> :smile:

Sounds like optional closing braces solves all problems.