Feedback sought: Optional Braces

Fair enough, just getting flashbacks to having to manually create Java Builders to work around the lack of named parameters.

I get that harm mitigation is a reasonable thing1. I’m also concerned that the way it is currently manifesting is “how we can adjust the rest of the language to accommodate this,” rather than, “how can we mitigate risk by isolating this until we know more.”

I’m worried this whole mess is well on it’s way to snowballing from “hey, what do you think of this experiment,” to frantic patches touching every other aspect of the language, in ways that are going to make my life much less pleasant.

1 From what I can tell, we’re very much in “lipstick on a pig” territory :man_shrugging:

1 Like

Honestly none of you “weird” examples look bad to me. I don’t think I would find them confusing and/or unreadable. Sadly there is just too much subjectiveness in this topic. What “feels right” for some might be terrible for others and vice versa.

Mind you, the syntax used was this:

[details=(Minimized to make people hate me less)]
...
[/details]

Which is different than the gist you posted.

1 Like

So I just ran @bjornregnell’s survey in my company’s engineering team, with the same samples and prompts. These folks are basically the complete opposite of @bjornregnell’s students:

  • Generally 5-10 years in industry, early- to mid-career folks
  • Worked at multiple places in multiple languages before joining us
  • Currently using Scala day-to-day at work, but not “enthusiasts” in the language itself.

Current set of responses is:

Prompt Votes
Optional braces but add keyword with 1
Optional braces but add delimiter colon 17
Optional braces, nothing extra 4
“Why can’t they just stick to the old way” 19

(The last prompt was not asked, but came up organically and was voted up)

A lot of the concern around this effort is around the migration cost: migrating our backend systems (currently on 2.12), migrating Apache Spark (which we just got onto 2.12), which is what you would expect in an environment with a lot of stuff we maintain.

But assuming the last option isn’t on the table right now due to @odersky’s BDFL card, Optional braces but add delimited colon is currently the winner by an overwhelming margin.

7 Likes

Was it possible to vote for more than one option?

1 Like

Will it not be confusing that only givens are written with with but other stuff with a colon?

2 Likes

Surveys based on an extremely small syntax sample are fine, but don’t tell the full story. We’d need to see the opinions of people who have actually used the syntax for a while. But if everyone persists in preferring colons, then I’ll accept it as the clear winner!

4 Likes

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