Feedback sought: Optional Braces

There’s also the question whether with and braces together is supported:

class C[A](x: A) extends T with {
  def f = ???
}

It can be seen as optional with in addition to optional braces. Then there’s the question of given:

given C with {}
given C {} //  `with` elided
given c: C {} // named
given c: C & {} // abstract with refinement

Of course you cannot omit both.

.. might make a comeback later. Remember one of the proposed extensions after 3.0 is to allow optional braces after operators, and to have an operator for function application. So far we have “left pipe” <| as the leading candidate for that apply operator, but .. could be an alternative.

2 Likes

It’s supported. As to givens, the rule will be that the type of a given has to be a simple type, possibly with annotations. This means that

given c: C & { ... }

is illegal, but

given c: (C { ... })

will work and will define an abstract given with a refinement.

The complete syntax is as follows:

GivenDef          ::=  [GivenSig] (AnnotType [‘=’ Expr] | ConstrApps TemplateBody)
GivenSig          ::=  [id] [DefTypeParamClause] {UsingParamClause} ‘:’ 

AnnotType         ::=  SimpleType {Annotation}  

SimpleType        ::=  SimpleLiteral          
                    |  ‘?’ TypeBounds
                    |  SimpleType1
SimpleType1       ::=  id                             
                    |  Singleton ‘.’ id       
                    |  Singleton ‘.’ ‘type’    
                    |  ‘(’ Types ‘)’          
                    |  Refinement                            
                    |  ‘$’ ‘{’ Block ‘}’
                    |  SimpleType1 TypeArgs       
                    |  SimpleType1 ‘#’ id          

Fair enough to select with over : if you prioritize regularity over ergonomics + esthetics in this case. I can live with that, and learners should be ok (even if with is wordy).

Now I need to get hold of M4 asap to re-program my fingers and learn how to love this new syntax :slight_smile:.

Competing requirements need to be balanced and trade-offs are seldom perfect…

Thanks for your prestige-less attitude in the quest for finding good solutions to conflicting goals! :heart_decoration:

5 Likes

BTW: When is M4 out?

M4 should be out in the first week of February. We will have the with syntax in the nightlies starting tomorrow.

3 Likes

Can with be made optional in the future after the ambiguity issue is no longer a concern?

1 Like

Is optional braces defined in Scala 3 Syntax Summary?

BlockExpr         ::=  ‘{’ (CaseClauses | Block) ‘}’

I can not see any definition…
Are the rules difficult to describe in formal grammar?

Great! Trying out scalaVersion := dottyLatestNightlyBuild.get now!
Would the recent proposals by @smarter on simpler syntax in lamdas including

List(1, "a").collect(case x: Int => x)
Map(1 -> 2).map(case (k,v) => v)

be possible to include in M4 | RC1?

This often trips my students; it seems as having to sometimes change () to {} hinders them in their first steps towards being comfortable with anonymous functions and higher-order functions in combination. It does not seem uncommon that students with some experience of an imperative language seem to think that “ouch! it did not work with (case …) then I make a while-loop instead”…

Link to the post by @smarter Feedback sought: Optional Braces

2 Likes

Note that for this particular example you can already write:

Map(1 -> 2).map((k,v) => v)

Thanks to parameter untupling.

2 Likes

As long as it’s a single case, this looks like an easy generalization. For more than one cases, it looks more complicated / less regular to allow them in parnentheses.

1 Like

Is it too “irregular” to allow () for single cases specifically?

1 Like

It looks like I’m a contrarian here, but I prefer : over with.

Part of what I’m thinking is that if I was teaching Scala and had to introduce the following class, I would say, “Book is a class that has two constructor parameters, title and authors, and it has a var field named copiesSold and a public method named price”:

class Book(val title: String, val authors: ArrayBuffer[String]) {
  var copiesSold = 0
  def price = ???
}

So when I speak I don’t use the word “with” when talking about a class, instead I say that it “has these attributes and behaviors,” or maybe that it “contains” these. Since I don’t use with when talking about the class, it seems a little wonky (for lack of a better term) to use it in the code. (Though “has” is arguably a synonym for “with”.) Conversely, I like that a : can symbolize any phrase such as “has these attributes and behaviors.”

Aesthetically, IMHO, I also find that the word with looks like it’s dangling out there by itself for small class declarations:

class Book(val title: String, val authors: ArrayBuffer[String]) with
   var copiesSold = 0
   def price = ???

That makes me actually prefer with::

class Book(val title: String, val authors: ArrayBuffer[String]) with:
   var copiesSold = 0
   def price = ???

But I wouldn’t want that, so if we go with with, I think I’d always use end to make it feel more complete:

class Book(val title: String, val authors: ArrayBuffer[String]) with
   var copiesSold = 0
   def price = ???
end Book

As a result of all of that, I prefer just the :, which is more concise, and doesn’t have that dangling feeling:

class Book(val title: String, val authors: ArrayBuffer[String]):
   var copiesSold = 0
   def price = ???

Also, using a : feels just like you’re writing text, like Markdown:

A Book:
  - takes the parameters `title` and `authors`
  - has `copiesSold`
  - has `price`

In my own experience with Scala 3, : works really well for small classes, so I don’t use end to close the class. Then when a class starts getting longer, I do use end, and I still like it.

Also, I don’t feel any confusion with the type ascription issue that I’ve seen other people mention. In this context I think of the : as a container for what follows. It’s like we use => to mean “transformer” with HOFs and arguably with case statements inside match, but I don’t confuse that with using => for self-types.

Again, that’s just my preference. If nothing else, I thought I’d share an opposing viewpoint and the rationale behind it. :slight_smile:

1 Like

Optional brace can be done without change any language syntax, actually. Just use a font with transparent brace glyph. And then we could stop hearing things like Wait, isn't three space indentation a joke ?, and keep focus on things everyone cares, lsp server and compiler performance e.g.

3 Likes

You actually don’t need to use a special font if your editor supports changing the color of the braces to have the same color as the background. I have been using such a setting in IntelliJ IDEA for years. I like to call it “Hidden Braces”.

6 Likes

I agree with the wonkyness and I have the same feeling that speaking “class C has members …” is more natural. Therefore I’m considering to, if with finally wins because of the regularity argument, instead speak “class C declared with members …” and the the English feels right again.

BTW: Just reading the pre-print of the fantastic PINS-ed5 and I’m trying to imagine with instead of : and hoping the itch with with would go away after eating a lot of pages :wink:

Which is why optional braces should be just that: optional. There should be no “with”, no “:”, there should be nothing at all but identation. I am not sure why this has been entirely dismissed as an option. All of this debate about strange keywords or symbols would be over should we just agree on the real purpose of optional braces, I.e., that braces are optional (rather than replaceable by a keyword and an optional “end” tag).

1 Like

It has to do with ambiguity and risks of mistyping white space, but I have not seen the detailed explanations with examples of this. Perhaps someone here can summarize the problems of having no token just indentation? @nafg Also raised the question if we later can drop the extra cruft if ambiguity issues are settled. But I guess the problems are perhaps not going away?

There is certainly something to keeping it that simple (although I suspect it is not possible due to possible ambiguity in some situations, even though I can’t think of any right now). I believe Odersky suggested a no-marker variant earlier in this thread, but some issues where found with it which made him reject it.

The optional end-marker could perhaps be added anyway (even if the braces where simply made optional without requiring a start-marker) to improve readability of larger code blocks.

1 Like

See:

2 Likes