The following code works as (I) expect in scala2 but generates a compile-time error in scala3. I tried to find an explanation in Programming in Scala, Fifth Edition and the web but failed. Any insights?
object Main {
def main(args:Array[String]): Unit = {
val foo = new Foo(7)
println(foo.double) // 14
val bar = new Foo(8) {
def triple:Int = this.i * 3
}
println(bar.double) // 16
println(bar.triple) // 24 in scala2; compile error in scala3
}
}
class Foo(val i:Int) {
def double:Int = i*2
}
Compilation error:
-- [E008] Not Found Error: /tmp/example.scala:12:20 ----
12 | println(bar.triple)
| ^^^^^^^^^^
| value triple is not a member of Foo
If you compile your code with scala -Xprint:typer
, you'll see that val bar
now explicitly has type Foo
in Scala 3:
def main(args: Array[String]): Unit =
{
val foo: Foo = new Foo(7)
println(foo.double)
val bar: Foo =
{
final class $anon() extends Foo(8) {
def triple: Int = this.i.*(3)
}
new $anon():Foo
}
println(bar.double)
println(bar.triple)
}
Contrast this to Scala 2, where bar
gets the refinement type Foo { def triple: Int }
:
val bar: Foo{def triple: Int} = {
final class $anon extends Foo {
def <init>(): <$anon: Foo> = {
$anon.super.<init>(8);
()
};
def triple: Int = this.i.*(3)
};
new $anon()
};
So, it's
val bar: Foo // In Scala 3
val bar: Foo{def triple: Int} // In Scala 2
To achieve the behavior of Scala 2, you could introduce a new class Bar
explicitly:
class Bar extends Foo(8) {
def triple: Int = this.i * 3
}
val bar = new Bar
Full compilable code:
object Main {
def main(args:Array[String]): Unit = {
val foo = new Foo(7)
println(foo.double) // 14
class Bar extends Foo(8) {
def triple: Int = this.i * 3
}
val bar = new Bar
println(bar.double) // 16
println(bar.triple) // 24 in scala2; compile error in scala3
}
}
class Foo(val i:Int) {
def double:Int = i*2
}
As for the reason why it's inferring Foo
instead of Foo { def triple: Int }
- I'm not exactly sure, I guess they just figured out eventually that this behavior plays better with other language features.
How would you explain that val x = "hello"
infers x: String
and not x: "hello"
?