Feedback sought: Optional Braces

After having mulled over the question for some weeks, I am still very happy with the outcome we have achieved except for one point: I am getting more doubts that the choice of : over with was the right one. So, forgive me to restart this thread with another exceedingly long post.

To summarize my previous evaluation:

Points in favor of : :

  • Shorter, quieter, and often more pleasing typographically (exception: classes with complex parameter or parent lists, where closing with with works better)
  • Learners seem to prefer it
  • Visually less of a departure from current Scala 2 style

Points in favor of with :

  • The language grammar becomes more regular, and will be easier to explain.
  • The visual appearance also becomes more regular, at the price of more verbosity.
  • We are true to the idea of optional braces (since with can also appear in front of { , but we are
    not suggesting that for : ).
  • no “false friends” wrt to Python, where people expect : for indentation everywhere once they have seen them for classes.

The danger I see is that we let ourselves too much be guided by first impressions. That’s basically what polls measure, after all. The other danger is that we look mostly at simple use cases, where : feels most natural. I tried to do a more detailed analysis of various use cases:

    object foo:
      ...

vs

    object foo with
       ...

Clearly, : is nicer. Same for class, enum, trait.

    class Foo(x: Int):
      ...

vs

    class Foo(x: Int) with
       ...

It’s a toss up, IMO. The : feels a bit too much like a type ascription here. On the other hand, the with is still more bulky.

    class Foo(
        x1: T1
        ...
        xN: TN
     ):
       ...

vs

     class Foo(
        x1: T1
        ...
        xN: TN
     ) with
       ...

with wins here. Like @ichoran said, ): is a sad face.

    class Foo extends Bar:
      ...
    class Foo(x: T) extends Bar(x):
      ...

vs

    class Foo extends Bar with
      ...
    class Foo(x: T) extends Bar(x) with
      ...

I think with wins again, in particular if the extended reference is more complicated than just a single identifier.

But how often do they occur in practice? To find out I did a scan of our community build, which consists of a number of standard libraries and their tests (more than 1M lines total).

    (object|class|trait) foo {

(where : clearly wins as a replacement) had 27416 hits.

    (object|class|trait) foo (...) {

    (object|class|trait) foo [...] {

(where I think it’s a toss-up) had 6270 hits.

    (object|class|trait) foo ... extends ... {

(where I think with works better) had 37400 hits

So in practice there are at least as many cases where with would work better than cases where : works better.

This means that preference of : over with might be mostly due to initial familiarity and concentration on simpler use cases. On the other hand, the consistency arguments in favor of with are indisputable. I also think that the “false friends” problem wrt to Python will be an issue. People who see : after an initial class might expect : to start indentation elsewhere, even though that’s not the case.

The fact is that everywhere else the indentation scheme used in Scala 3 is much more like the one in Haskell than the one in Python. So, it might be better to “stick to our guns” and follow the same syntactic tradition everywhere.

It would mean that seen in isolation, class syntax becomes more bulky and at least initially less familiar to most readers. But in the overall scheme an extra keyword for classes does not really matter. And the consistency arguments feel more important. (and, of course, as always, if you don’t like it you can just stick to braces).

23 Likes