Feedback sought: Optional Braces

In my student survey on Discord the 3 options where stated to be exclusive in the poll instructions, but due to how Discord works with “reactions”, where people just can easily click on any of three “reactions” that I had prepared, I actually don’t know if someone voted more than once as only a summary number together with the nick names of the first couple of clickers on each reaction is given by Discord, but I do believe my students did comply with the instructions given to only vote on one alternative. It’s of course not a scientifically controlled survey, just an indication…

However, I do think it is a reasonable hypothesis that with is not very popular as the result of this non-scientific poll so clearly is against it.

Hi. I teach introductory programming at university; I’ve taught using Scala since 2013. Inspired by @bjornregnell’s post, I decided to poll my students too to add a data point.

This is another non-scientific survey. I gave them three versions of a piece of code, analogously to what Björn did. They could vote for exactly one preferred option once (and had to be logged in to do so). The students were already on their holiday break, but I managed to get 65 answers over the last couple of days.

This cohort is, generally speaking, quite inexperienced. They have just finished a programming course, but most have limited (or no) experience of programming beyond introductory-level stuff. Very few have any professional experience. I expect that only a tiny minority will have appreciated any broader consequences of this syntactical choice; this was just a poll about immediate surface-level reactions, really.

The results:

  • 9 people (14%) preferred option #1 (with).
  • 33 people (55%) preferred option #2 (colon).
  • 23 people (35%) preferred option #3 (nothing).

This poll for novices is only what it is. But it does suggest that if with is picked, educators may need to be particularly careful to convince students that it’s a good pick.

2 Likes

I agree with @LPTK. These surveys are useful, but they don’t tell the whole story. I understand that colons might appear more aesthetically pleasing to bigger crowds on a glance. I am just not convinced yet that that same perception remains after having used it extensively in real world code bases. Especially if given and potentially anonymous classes will not be consistent with this choice. However we won’t know for certain until a final choice has been made and we start using it in the wild to gain that experience.

I don’t believe there are any new arguments anymore in favor of either. So I also accept whatever will be chosen.

3 Likes

IMO the goal should not be to pick something that is the ideal of the majority, it should be to pick something that will result in the least amount of negative reactions. If we’re confident that none of the options will produce major negative reactions, or that they are equal in that regard (including the option of not having optional braces), then the majority preference is a good tie breaker. But IMO the most important thing is that we don’t pick something that will result in Scala getting more negative press, within private companies, in the blogosphere, and on reddit. In too many circles Scala has a bad name that prevents people who would benefit greatly from it from even seriously considering it, and IMO the most important thing is how well Scala 3 fixes that.

3 Likes

I tried following this thread, but if there ever was a specific reason or argument given for not further pursuing .., I must have missed it. Or has this not been addressed yet?

1 Like

:star_struck:

This is making me really happy, thank you very much for this! I think |> and <| will be great additions to Scala. (Much better than Haskell’s & and $, since these pipes make it unambiguously obvious what’s going on.)
Not having them available requires putting parentheses/braces around things everywhere all the time and that can get bothersome really fast.

Fira Code :point_left: Look no further. Since there are already many languages that use |> and <|, this is an established idiom. (Alternatives)

2 Likes

Making a poll, in this case about :, is a very clever idea :+1: the more input is available, the better the chance that a good decision is taken. On the other hand, we can’t expect to be able to create a great thing just by divide & conquer – dividing it into sub-components, choosing the most popular variant for each and then putting them back together, expecting to arrive at the optimal whole. The sub-components aren’t really independent, they have overlapping concerns, so an improvement in once can cause issues in another. These polls have shown it well, I think.

Instinctively, I and many other people would prefer the variant without : nor with. It would be the most elegant thing ever. Yet, AFAIK, it’s not possible, because it can be ambiguous.

Many other prefer : for opening a block. We could speculate whether it’s because of how widespread Python is. Yet it is problematic, because we already use : for type annotations, even for function returns, which is not what Python does (it uses ->). At least, it could look weird/confusing. At worst, could it also be ambiguous? IIRC, the main offender here was the given syntax.

It’s also worth pointing out, that none of the snippets shown to the poll participants contain an example showcasing why a : or a mere \n could be a bad idea. In that light, the poll results aren’t as surprising. It was also interesting how programmers with deep Scala 2.x (or other languages) experience see things vs. the beginning programmer (familiar with Python? or nothing al all?). But Scala obviously needs both groups, loosing established professionals would mean a collapse, while not attracting young new developers means slow decay.

This all makes me lean towards with.

It’s also worth repeating @regiskuckaertz 's original suggestion that = could work. It might be the most natural solution after all :thinking:

3 Likes

I think it makes perfect sense to allow optional braces after any operator. I hope this will include non-symbolic operators. This way, one can define an of operator, useful for its low precedence, to do the same as the proposed <|, as in

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

If we go to the logical conclusion of this approach, we can arrive at a definitive fix for postfix operators: indentation lets us determine what is or is not an argument to trailing operators:

foo tap
  println

// i.e.,

foo.tap {
  println
}
xs map f toSet
ys

// i.e.,

xs.map(f).toSet
ys
xs map f toSet
  ys

// i.e.,

xs.map(f).toSet {
  ys
}
6 Likes

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.