class Seq
trait IterableOnceOps {
def toSeq: Seq = new Seq
}
trait IterableOps extends IterableOnceOps
trait SeqOps extends IterableOps {
def toSeq: Seq
}
class Foo extends SeqOps
The code snippet above compiles with Scala2 but not Dotty:
-- Error: tests/allan/Test.scala:53:6 ------------------------------------------
53 |class Foo extends SeqOps
| ^
| class Foo needs to be abstract, since def toSeq: => Seq is not defined
one error found
This pattern is used in the 2.13 collection library
cc/ @julienrf
Followup, If I do decide to implement toSeq in Foo, should I be required to add the override modifier:
class Foo extends SeqOps {
def toSeq: Seq = new Seq
}
tests/allan/Test.scala:54: error: overriding method toSeq in trait IterableOnceOps of type => Seq;
method toSeq needs `override` modifier
def toSeq: Seq = new Seq
^
one error found
h in this example from https://www.scala-lang.org/files/archive/spec/2.13/05-classes-and-objects.html#overriding:trait A { def f: Int }
trait B extends A { def f: Int = 1 ; def g: Int = 2 ; def h: Int = 3 }
trait C extends A { override def f: Int = 4 ; def g: Int }
trait D extends B with C { def h: Int }
Also, the text which defines overriding never forbids this scenario:
This definition also determines the overriding relationships between matching members of a class C and its parents. First, a concrete definition always overrides an abstract definition. Second, for definitions M and M' which are both concrete or both abstract, M overrides M′ if M appears in a class that precedes (in the linearization of C) the class in which M′ is defined.
Foo.toSeq overrides an existing implementation, so it seems override should be required as usual to ensure that the override is desired — luckily, that's even clear in the spec (https://www.scala-lang.org/files/archive/spec/2.13/05-classes-and-objects.html#override).https://www.scala-lang.org/files/archive/spec/2.13/05-classes-and-objects.html#overriding
Apparently this is an intended change and matches Java semantics (though Martin didn't go into detail), so the spec should be updated and the compat:Scala2 label stays.
We should also see if we can make a rewrite rule for this.
Apparently this is an intended change and matches Java semantics (though Martin didn't go into detail), so the spec should be updated and the compat:Scala2 label stays.
This came up in the context of https://github.com/scala/bug/issues/12224 (ty @Jasper-M!). Could you go more into detail on why this was changed? In https://github.com/lampepfl/dotty/issues/2072 it seemed like it was understood to be an issue in Dotty, but then it just fizzled out...
I remember learning from this SO answer:
which now compiles on Scala 3, as promised in 2012:
trait A {
def ping = println("ping")
}
trait Cake {
type T
}
trait S { this: Cake => // S with Cake
//type T = A // concrete overrides abstract
type T <: A // but abstract was overridden by abstract in linearization order; loses the bound in Scala 2
def t: T
def f = t.ping // was: error: value ping is not a member of S.this.T
}
Just to document this related change here.
ping @odersky when you get a second, do you remember why this is an intended change in Scala 3?
Because it's what Java does, as well as what the JVM does for default methods IIRC.
Any ideas why Java does it that way? Is there a reason why this is bad? For instance, in the n years that this has worked this way in Scala 2, has caused an issue? Or perhaps there's implementation reasons behind this?
I have no idea.
Only discussion related to this that I can remember is https://contributors.scala-lang.org/t/ability-to-make-a-parents-concrete-method-abstract/1255
It's also pointed out in that thread that Scala 2 already does the "Java thing" when it comes to abstract classes.
scala> class Baz { def foo: String = "string" }
class Baz
scala> trait Foo extends Baz{
| override def foo: String
| }
trait Foo
scala> new Foo{}
val res5: Baz with Foo = $anon$1@4374e0
scala> abstract class Bar extends Baz{
| override def foo: String
| }
class Bar
scala> new Bar{}
^
error: object creation impossible. No implementation found in a subclass for deferred declaration
override def foo: String (defined in class Bar)
In case that's news to anyone.
Scala 2 allows widening the type in an abstract override, but that seems a pretty obscure feature
scala> trait T { def f: String = "" }; trait U extends T { override def f: Object }
trait T
trait U