Class declaration using comma instead of with

It seems Scala 3 supports declaring classes and traits with comma instead of with, as in:

class A extends B, C, D

instead of

class A extends B with C with D

I do not see this described in Classes & Objects | Scala 3.4. The only mention I have found about this is in The status of `with` in Scala 3 - Language Design - Scala Contributors, where it states:

For the moment, the dotty compiler accepts both , and with after extends . I argue that we should switch to ,

Also the topic discusses creation of anonymous classes, with various possible options to avoid with in this situation, but I guess there was no consensus on any of them?

1 Like

I had no idea that Scala 3 was still in such a state of flux.

Despite my initial reluctance to get into Scala 3 - seemed like a load of make-work for end users at the time - I was won over on the premise that things would be settled down.

It seems not!

Have to say, I have grown to like the new Scala 3 syntax, and am mildly enthusiastic about the deeper changes and more principled replacements for implicits, etc.

The conversation recently about matchless cases seemed reasonable; it’s trying to plug a hole in the language specification and compiler implementation - the syntax itself wasn’t really being changed. An improvement, or a fix.

If folk want to make the case for adding new syntax, fine. Likewise, adding in direct-style support, sounds like it might make for easier to read and write code. Cool.

This comma thing just seems like faffing around for the sheer hell of it.

Could we not do a Fortran 77 for a few years here and just write some code?

The commit was in Jan 2019, which suggests less flux than etched in stone.

Two random edits from the last year demonstrate a generous Swiss tolerance:

image

image

There are syntax challenges coming to light due to named tuples, similar to when Scala 2 disallowed an assignment expression as an argument to a method invocation (which looks like a named arg).

One is named arg in infix application: x op (y = z). Infix with multiple arguments has been unsettled syntax.

Another is x => (y = z), written instead of x => { y = z }.

I think all this demonstrates how much labor goes into “mere syntax”. I don’t think it can be dismissed as faffing, although Scala was invented as a hybrid of OO and faffable programming methods.

This feature has been in Scala 3 from the start. And it’s documented in the official syntax summary.

Somehow it never got a page explaining the change, probably because it is so small, The official Scala Spec is also behind, and needs to be updated. For instance the chapter on classes and objects should mention derive clauses, but they are omitted as well. I think the spec should be labelled for the moment as a work in progress.

5 Likes

Fair enough, thanks to both of you for the clarification.

See it from my point of view, though - I’ve been told to use with, grown to like it - to the point where I’m writing such gems as:

  def withRecipe(
      recipe: String
  ): JavaTrialsScaffolding.SupplyToSyntax[Case]
    with ScalaTrialsScaffolding.SupplyToSyntax[Case] =
    new JavaTrialsScaffolding.SupplyToSyntax[Case]
      with ScalaTrialsScaffolding.SupplyToSyntax[Case] {
.....

Now, my IDE is telling me I should be using & instead of with for the return type declaration - this is a Scala 2.13 / 3 cross-build, although it’s not telling me that the anonymous class instantation shouldn’t be using with.

I think it’s fair to say I’m going to have to move with the times and use & when I’m in Scala 3 only for types - if that’s what we’re moving too, but it seems the syntax is swinging around too much. Should I use ,, & or with for types? What about for inheritance?

If you say “stick with with for both types and inheritance if you’re old-fashioned, or use & for both type and inheritance if you’re bold”, then fine.

Maybe I’m just behind the times; did this surprise anyone else?

The reason with was split into “&” and “,” is that they actually have different meanings in Scala 3:

  • A & B is a type which is a super-type of both A and B, and is equivalent to B & A
  • T extends A with B means T will inherit all methods of A, and from B only ones not shadowed by ones in A (or something like this, I can never remember), this is not at all equivalent to T extends B with A
  • new A with B declares an anonymous class which would otherwise be Anonymous extends A with B, so is not equivalent to new B with A for the same reason as above

In Scala 2, A with B as a type is not equivalent to B with A, so there was no reason to differentiate the keywords

The reason your IDE prompts you to use the type A & B instead of A with B is that they are aliases in Scala 3.
In other words, when you write A with B in Scala 3 you get a fundamentally different type than A with B in Scala 2
(But these types have many similar properties, which is the Scala 2 version was not ported to Scala 3)

For further research, Scala 2’s A with B is called a compound type [spec], whereas Scala 3’s A & B is called an intersection type [spec] [reference] [Scala book]

As for T extends A with B, that is a class template, where A is the superclass, and B is a trait reference [Scala 2 Spec] [Scala 3 Spec]

I must admit the confusion is not helped by this article in tour of Scala

1 Like

Thanks, @Sporarum.

I was aware of the linearization order aspect when the with syntax is used, but truth be told, if pressed I would have assumed that A with B is equivalent to B with A when used as types (as opposed to inheritance specifiers).

So maybe I’m right about the type usage being commutative, or if I’m wrong, does that mean that the ability to define an A with B that is not the same as an A & B will vanish if with is removed?

Not sure whether that would be any great loss, given I have no idea what the difference would be, but…

EDIT: other than being unable to bind an expression that isn’t literally extending A with B to a name of type A with B. I can see the preference for &, if so.

That is true in Scala 3, but not in Scala 2 (which is why we want two different keywords)

No, A with B is equivalent to A & B in Scala 3 !
The old behaviour is only available in Scala 2

The choice to allow A with B as a type in Scala 3 was to make porting code easier, even though the kind of type this notation signifies, compound types, do not exist in Scala 3

1 Like

That was educational, as usual.

I notice that the referenced discussion hasn’t progressed since 2020, so perhaps I shouldn’t worry unduly after all.

It seems that , can’t be used either for types, so in a sense the battle is between with and , for inheritance specifiers (with the caveat that with can be used as a misnomer for an intersection type for fogeys like me).

Perhaps it would be easier to keep with for inheritance - and maybe deprecate the use of , (and issue a warning about with in a type). Would that clear the way for named tuples to shine? Why was that , syntax introduced anyway? My IDE is happy with with as an inheritance specifier in a Scala 3 project… :slight_smile:

Anyway, I’ll go with the flow, thanks all.

1 Like

As a follow-up, I saw the announcement about Scala governance and release policies, specifically the link to Scala development guarantees which covers source compatibility and mentions the Open Community Build.

That sounds like good news - in particular, it settles a nagging doubt in my mind as to the prevalance of usage of with versus , for these pesky inheritance specifiers (or derive clauses, if that’s the official terminology). Essentially, whatever is in common use will show up in the community build, so it gets some representation in deciding what is considered a breaking change regarding source compatibility.

I understand that there is still some margin for making breaking syntax changes in this scheme, but it isn’t just a case of firing at will, which is reassuring.

2 Likes