Feedback sought: Optional Braces

I really like this. I think I’ve proposed it before.

And it’s really the same as :, just tipped sideways: ..

If we were going to throw * away as varargs and use ... instead, then I’d hesitate. And if it were Rust, which uses .. for ranges, I’d hesitate. But for Scala I think it works quite well.

I am not sure I like it any better than with, but if people find foreach with weird, maybe they’ll be okay with foreach ..? I would be.

Note–this may seem like a bunch of bikeshedding and not super-related to whether optional braces are okay.

But optional braces are entirely about bike sheds, not about how the bike works, so it can make a big difference whether we like the color. So to speak.

6 Likes

actually, with the explanation that .. is just : on its side, it looks strangely attractive… I will need to see how it works on a full example, but intriguing.

Of course it’s on its side! It has to point downwards towards the indented lines below…:rofl:

6 Likes

Generally, I think the idea of braceless syntax has a lot of potential to clean up the language, so I support the exploration.

However, I think the current implementation needs more work. Having multiple ways to open a new block, namely :, with, do, a newline, or then, is very inconsistent when compared with the status quo of using { everywhere. When I code, I need to think about my problem space, not syntactic rules. Muscle memory is really important here, and that inconsistency would destroy it. The fact that two of those block-openers are overloads of existing syntax (with and :) makes it even worse. I know that these changes are categorized as different features, but it seems they are not independent from each other given the discussions in this thread and elsewhere.

I think that it was a mistake to write books, record MOOCs, etc. based on an unfinished syntax that was subject to change. But the only way to make that mistake worse would be to double down and use those sunk costs as an excuse to force an inconsistent syntax on the language.

There are, then, two choices for going forward:

  1. Quickly resolve the inconsistency above, in time to ship. Personally, I like the idea of doing away with :, with, do and then completely, although I recognize that it’s probably significant work to do so, and I’m somewhat ignorant of the implications.

  2. Admit that braceless syntax needs more time to bake, and that shipping Scala 3 doesn’t require it. Make the braceless syntax an experimental feature, turned off by default. Continue to iterate on it, and include it in a later release.

Both options require some adjustment to documentation, materials, etc. I know that @odersky already said 2 is off the table due to the large amount of work to rewrite various docs, but all the adjustments proposed here require rewriting/re-recording anyway. Drawing a line between existing-brace-syntax and adjusted-braceless-syntax seems somewhat arbitrary.

Regardless of my specific syntactic concerns, I think that one thing that is indisputable is that this is a highly controversial subject in the community. I am concerned that it will lead to two camps of Scala 3 devs: those writing braces and those not. Given that a major language version with significant other changes already has the potential to split the community somewhat, I think that creating a third group would be a mistake. This is another argument for option 2 above.

Thanks for the opportunity to provide feedback, and I’m looking forward to Scala 3!

5 Likes

This is a great idea! I feel though the example would be even stronger visually without “overloading” (:wink:) ... for “whatever”:

:exploding_head:

1 Like

That argument has been put forward often. But in my experience it applies only in a very limited way that can be solved by careful design. A good counter-example is Python with type hints. It also uses : in both roles, and if I look at typed Python code that does not seem to present a problem at all.

1 Like

That may be because they use -> instead of : to indicate return types, which is where the ambiguity would normally arise:

def munge(input: AnyStr): ...
def munge() -> PosInt: ...
2 Likes

Exactly. Analogously, in Scala, a : after a method parameter list cannot possibly start a block, and the -Yindent-colons extension knows that. So there is no ambiguity either.

def f(x: Int): 
  Int = ...

would always be treated as a type ascription. That’s what I meant when I wrote that the ambiguity can be avoided by careful design.

To clarify, the ambiguity I was referring to is “visually ambiguous for the humans”, not “semantically ambiguous for the parser.”

This concern doesn’t keep coming up because we think the compiler won’t be able to keep track of them. It’s because we’re worried the humans won’t be able to quickly parse them, so we’ll have to stop and think about the particular context, rather than the problem we’re trying to solve.

10 Likes

I know that having more choice isn’t always helpful and can often be regarded as a poor compromise, but another choice could be to allow : or where to introduce a block.

Personally, for any classes/objects/… that are more than a few lines then I would probably end up using brackets or perhaps where if it was an option.

For definitions that are really short/simple then the colon seems like it could work okay. I also agree that do and with don’t work well for function parameters.

1 Like

Of course. But would anyone write

def f(x: Int):
  Int = ...

? I think I have seen that exactly once in about 1.5 million lines of code. So I think we are safe. Visually, : at the end of line means “omitted braces”. People will internalize it and even if they need to write a return type on a new line prefer

def f(x: Int)
  : Int = ...

which is already now the preferred form, by far. So we are unambiguous both for the grammar and the visual perception.

I have been thinking why : turned out to be better than the alternatives. I think it’s because it is closest to the spirit of “optional braces”, i.e. it blends best with current Scala. There was a comment by @deusaquilus earlier that said that people don’t even see the difference for a while until it is pointed out to them. That means that, even with : it’s still clearly and recognizably Scala, whereas if we use something heavier like with or where that uniformity is lost to some degree. That’s probably what was most appealing about :.

[As I wrote earlier, we tried out several other alternatives with a very open mind. I believed they would work better and wanted them to succeed. But it did not work out. So, while the conclusion is ultimately subjective, it is also based on empirical evidence.]

2 Likes

Of course, you can figure out if : is type ascription or block start, but it takes extra mental effort. Readable means understandable with little effort.

I love the double-dot .. proposal.

The real experiment is whether all those people who profess to enjoy writing code with the new syntax will also enjoy reading that same code a few months and hundreds of lines later.

After all, back in the days when I wrote my first code in Basic using GOTO as the primary structure element, I did enjoy writing code. And the syntax was so clean. No braces!

3 Likes

I personally find it very ugly, but I know that auto-formatting tools (including scalafmt) sometimes do that when the line is getting too long.

I also also to echo that I do not like the colon being used for types and indentation scopes.

1 Like

For example, compare these two:

def supercalifragilisticespialidocious(foo: Foo, bar: Bar, baz: Baz, bang: Bang, boom: Boom):
Supercalifragilisticexpialidocious.PrettyLongAndSomewhatUnwieldy

supercalifragilisticespialidocious(foo: Foo, bar: Bar, baz: Baz , bang: Bang, boom: Boom):
Supercalifragilisticexpialidocious.prettyLongAndSomewhatUnwieldy

Other than my difficulties reading the new syntax, one of my biggest concerns is that you appear to be genuinely surprised that anyone would dislike the new syntax. I worry that “this will succeed” became an axiom at some point, and confirmation bias has set in.

If that’s the case, then there isn’t anything I or anyone else can say that will move the needle on this, though we’ll likely continue to try :man_shrugging:

As the testbed for this feature (outside of the compiler project) appears to have been primarily students, doesn’t the number of full-time developers who’ve bothered to say, “hey, I’ve got concerns about this,” give you at least some cause to reconsider if this is actually a good an idea? If not, perhaps it’s worth considering.

Particularly because, when compared to the last minute overhauling that given has received, based on far fewer voices raising concerns about as, it seems like the bar for change even being considered is set much higher for this.

4 Likes

First of all, thanks for making this thread. It’s good not to close the discussion yet. I can’t say anything on the experience of writing with the new style, as I haven’t done so. Thus, I only read the code examples so far. @lihaoyi has well summarised the trade-offs of the various variants, which I agree with—the goals are conciseness, consistency, unambigousness.

I have also tried all possible characters and names proposed, tried a few more like [] or _ or ~ or --. The good candidates were : and \ in my opinion. Until I saw the double period .. proposal of @Mocuto . I think that is a very good idea, and it might be worth playing it through all possible scenarios. I read it as an ellipsis that indicates a pause after which something is to be expected to follow. It is thus semantically clear. It is visually unobtrusive. It’s short and very easy to type. It can’t be valid syntax otherwise.

Screenshot from 2020-12-10 11-59-12


Regarding number of different cases: I would suggest making extension and given akin, putting with at the end of the first line in both cases. And couldn’t with for mixin be removed again for comma, like class Foo extends Bar, Quux?

3 Likes

I really like .. for terms.

For template definitions (classes, objects, givens, and maybe extensions), I still prefer with by far. The relationship with mixing composition is not an accident – it’s perfectly adequate: you mix some definitions into a class/object definition. As people have already pointed out, things like Foo with { val x: Int } were already valid syntax in Scala 2, so it’s completely natural to extend that to the indentation marker use case.

8 Likes

In light of this and the fact that, among others, @jdegoes and @LPTK have mentioned this syntax as desirable, can I bring attention to my earlier very ontopic question which got drowned out a little in all the other feedback?

Actually, I can agree with that. Also template definitions are far less in number compared to the terms, so the extra costs of the visually not unobtrusive with might be justified.

4 Likes

I kinda like this syntax, but this feels like too many keywords and I feel like it’ll turn off too many people (people new to Scala entirely and people used to Scala 2). Also, do feels too imperative (as was mentioned above). Also, using = instead of where in your given examples might be a bit better. I don’t know about other people, but they looked the same to me at first. The distinction between = and with is immediately clear, the distinction with where not so much.