Feedback sought: Optional Braces

Thanks for reusing and replicating this study! Very interesting to see the same pattern with different types of subjects.

Regarding the “stick with the old way” wish: I think being conservative about changes and always being backwards compatible is a key part of Java’s industrial success.

Hopefully, the fact that most of Scala 2 works under Scala 3 will make migration easier. And braces will probably “never” go away… Evolution rather than revolution is probably always more appreciated by stakeholders with existing code in production.

Thanks for reusing and replication the study! Very interesting to see the same pattern; and you got an abundance of responses! :slight_smile:

Side note: I’m also very glad to learn about another university in a neighbor Nordic country using Scala as first langue! It would be nice to get in touch with you after xmas hollidays and share experiences in introductory programming teaching with Scala. Here is the home page of my course (in Swedish): http://cs.lth.se/pgk/ and here is the open teaching material: https://github.com/lunduniversity/introprog

Merry xmas at Aalto! :santa:

2 Likes

I would also fine with = as open block marker. My primary concern is consistency. In Python I can simply hit : at the end of the line to start a block and I’ll be fine, no matter if I define a class or specify method or write an if statement. I don’t want to have to reason about the intricacies of syntax with regards to the definition of various language constructs (too much).

I don’t think a reason was given for .. or = to be discarded as possible choices.

To be clear, in my view blocks and definitions are two orthogonal things. I proposed = as the universal identifier for introducing definitions. For blocks, the examples I have seen so far fall under two categories: (1) those introduced by control words (if, while, etc.) which are well-defined already, and (2) arguments to functions.

For the latter, and again this is my personal view so very biased, parentheses are unambiguous. And if the language is powerful enough, they can be removed by introducing a combinator with the proper fixity. If I refer to the Scala rules for operator precedence and associativity, it looks like :| and |: would be good candidates.

To take @LPTK’s example:

def constrExpr(): Tree =
  if in.isNestedStart then
    atSpan(in.offset)(
      inBracesOrIndented (
        val stats = selfInvocation() ::
          if isStatSep then { in.nextToken(); blockStatSeq() }
          else Nil
        Block(stats, unitLiteral)
      )
    )
  else
    Block(selfInvocation() :: Nil, unitLiteral)

or

def constrExpr(): Tree =
  if in.isNestedStart then
    atSpan(in.offset) |:
      inBracesOrIndented |:
        val stats = selfInvocation() ::
          if isStatSep then { in.nextToken(); blockStatSeq() }
          else Nil
        Block(stats, unitLiteral)
  else
    Block(selfInvocation() :: Nil, unitLiteral)
2 Likes

Thank you all for your inputs and in particular the surveys. Amazing that we can turn a hot and opinioned debate into something that approaches empirical language design! This was actually very productive and informative.

The hardest decision was whether to go with with or remain with :. In the end I think we do not have enough arguments to switch. with is theoretically cleaner, but : seems to be more legible and appealing in practice.

I identified several other action items.

  • Try to nudge the ecosystem towards an indent margin of 3 spaces instead of 2. To make a start I have reformatted the Scala 3 reference pages with this margin.

  • Look into a tightening of the layout rules. This will have to come over time. For 3.0, the focus should be on compatibility with legacy Scala-2 code that is sometimes indented in quite peculiar ways. After all, we explicitly do not want to have a “mode switch” that turns indentation on. Indentation is significant even inside braces. So we have to be very lenient to accept a large variety of layouts for now.

  • Replace the -Yindent-colons scheme with new layout rules that allow to omit braces around closure arguments and after operators at the end of a line. This is also something for after 3.0.

Meanwhile, happy xmas to everyone!

8 Likes

Which brings us back to this key question:

But it is easily answered if abstract given refinements are now denoted as &.

Merry Christmas

2 Likes

I’m fine with the outcome of this discussion not being be personal preferred choice, but I must object to the usefulness of these unscientific polls. The conclusions that can actually be drawn from these polls are severely limited.

First of all, they don’t add anything we don’t already know, since you already tried out these things with your students in more detail.

Second, the only thing they are actually evidence of is that people find : the most obvious choice.

Third, people voting for one thing is not evidence of actively disliking the other options.

Four, these polls don’t adress the concerns people have raised about the use of :. In particular, these snippets do not in any way suffer from the issues that have been raised. As I’ve already said multiple times, : is not an issue in cute poster child Scala syntax.

If these polls was all that was needed to decide this issue, why did we even have this discussion?

A real study would have tried to measure which syntax is easier for people to read and comprehend, not just ask for people’s immidiate subjective reaction to simple examples.

I think the compromise you proposed earlier is the best way to make everyone come out of this feeling that they got something:

In the general case with is used to start a template body, and in the case of class, object, trait and enum, : can be used as a shorthand.

This would adress the very legitimate concerns that has been raised about using :, while still allowing it where some people feel that a keyword is too long.

The only thing not solved by this solution is how to distinguish type refinements from structural instances. But maybe it’s better to require braces for type refinements anyway, which seems to be the current implementation.

5 Likes

I personally find no material difference in the readability between 2 and 3.

From the tooling perspective, I think it would introduce friction and confusion. Scala 3 source code will continue to use *.scala file extension, which most editors, linters etc would set the language-specific rules about how to format the file. Today, I set my editors to use 2-whitespace for tabs, and this would apply also to indentation / “outdentation” shortcuts.

Thus far, optional braces has been discussed as a superset of Scala 2-like syntax, allowing for gradual transition into the indentation style, potentially converting them method-by-method. 3-whitespace complicates this because we’d have introduce whitespace changes to the git history each time we introduce it to a file / subproject. And the editors or linters would have no information to tell if it should use 2- or 3-space indentation.

If we really must indent more than 2, I’d recommend 4 because I can at least keep using the existing keyboard shortcuts x2 times.

10 Likes

OK my feedback starting point. I think Scala 2s syntax is noisy, but not because of its use of braces. I think optional braces will make it noisier. I would suggest the length of this thread is evidence of the noisier nature of the new syntax and the fact that it is making the language more complicated.

Regardless of whether one uses braces or indentation or some combination of both, I just don’t agree with muddling up parsing and semantics. I don’t agree with keyword syntax.

alpha1 alpha2
alpha1 + alpha2

The fact that in the second line alpha1 and alpha 2 have a higher precedence relationship than in the first line, that space plus space is closer than a single space in my view is just simply broken. Apologies, but I don’t know how else to express my opinion other than in somewhat sharp terms. I don’t think the design of Scala’s syntax can be considered an example of good functional design.

1 Like

I don’t understand. Are you talking about how return 1 + 2 would be parsed as return (1 + 2) instead of (return 1) + 2? The former makes sense to me, and anyway, how often do you use keywords such as throw and return that precede other expressions?

Yes I think return 1 + 2, should be parsed as (return 1) + 2. At least if one were creating a syntax from scratch. Obviously Scala couldn’t just move to such a syntax overnight.

The first high level programming languages were much simpler affairs. They could afford to take a pretty cavalier and incoherent approach to the development of syntax. However modern high level languages are increasingly sophisticated and that inevitable means more complex. Nearly all developers these days want to programme in complex languages, much as they like to pretend they want simplicity.

Because we desire more sophisticated, powerful, succinct, expressive, and therefore inevitably more complex programming languages, it is vital to separate concerns and separate parsing from semantics. I would argue that the current situation not only creates a syntax that adds a significant cost to the human parser, but also does not use the syntactic space (I’m referring to the mathematical concept of space here, not to white space) particularly efficiently.

Its my guess or evaluation that we could have a syntax that was both more regular. allowed for more succinct expression of Scala semantics and could be used unmodified for our data as well.

Why should return 1 + 2 be parsed as (return 1) + 2, though? It would require you to write return (1 + 2) each time. To me, that is much less intuitive and useful.

I rather like Scala 2’s syntax, even though I’m still wary of Scala 3’s new syntax, and I can’t tell exactly what you’re referring to in your answer. What particular complaints do you have about optional braces?

I agree that 2 vs 3 doesn’t matter much.

2 spaces make it hard to tell just how indented something is.

3 spaces make it kinda hard to tell, and deeply indented stuff has too much whitespace in front.

4 spaces makes it pretty easy to tell, but deeply indented stuff is hardly even on the screen any more (at 80 characters–I frequently have indents ten deep, which is half the available space at 4-space indents!).

The way to solve this issue is to get scala syntax mode in editors to include stronger vertical patterning to indicate indent levels. Then you can tell how indented something is.

(“But it doesn’t work with every editor already!” isn’t a very good counterargument since a lot of editors already don’t expect 3-space indents.)

Indentation width is probably comparably contentious to discussions about maximum line lengths. Why not make the required/expected indent (2, 3, 4) a compiler-flag, with a rewrite rule to go from one to the other?

@odersky, would you please consider adding some rationale on why the .. proposal is not in the running anymore (apparently)?

I simply don’t think it would work better than what was chosen, and at this late point we should be way beyond considering more changes.

1 Like

Sure, that much is clear from the lack of answer, but it seems to me that the other proposals received more in-depth consideration (or at least responses), which, given the excitement that was there for its potential to break the stalemate, was surprising.

1 Like

4 spaces makes it pretty easy to tell, but deeply indented stuff is hardly even on the screen any more (at 80 characters–I frequently have indents ten deep, which is half the available space at 4-space indents!).

I kind of like how it pushes me to extract code into discrete methods. “And besides”, Python gets away with four spaces. Ancient Java/Go uses eight AFAIK.

Again, idiomatic Python code (imperative, statement-based) is extremely different from idiomatic Scala code (functional, expression-based). Python doesn’t even have multi-line lambdas, which would be absolutely unthinkable in Scala. So you can’t expect Python experience to apply to Scala here.

9 Likes