Scala 2:
scala> trait A { def x: Any }
trait A
scala> class B extends A { def x = "x" }
class B
scala> new B().x
val res0: String = x
Scala 3:
scala> trait A { def x: Any }
// defined trait A
scala> class B extends A { def x = "x" }
// defined class B
scala> new B().x
val res0: Any = x
So it seems that in Scala 2 the type of x
is inferred as String
, but in Scala 3 it is kept as Any
.
What is the reason for this?
I guess there is some good reason then. Too bad.
It’s not that big of a hassle to write:
class B extends A { def x: String = "x" }
But when you have something like this, it becomes mildly unpleasant:
class Wrapper[T](x: T)
trait A:
def tup: Tuple
class B extends A:
val a = Wrapper("foo")
val b = Wrapper(1)
val c = Wrapper("bar")
val d = Wrapper(true)
def tup: (Wrapper[String], Wrapper[Int], Wrapper[String], Wrapper[Boolean]) = (a, b, c, d)
A solution could be:
val temp = (a, b, c, d)
def tup: temp.type = temp
But it’s a bit ugly.
The Scala 2 ticket was finally closed as out of scope.
The common examples are
def f: Option[A]
and
override def f = None
default void remove() { throw new UnsupportedOperationException(); }
and
override def remove() = throw new UnsupportedOperationException
or override def remove() = ???
I wonder if there is a Scalafix lint for “inferred type of public member”, as several comments insisted it is a sin.
The Java example suggests that a minimal change would be not to narrow to inferred bottom types when overriding Java API.
Also maybe infer narrowed type if the method is effectively final; I don’t know if there are benefits when inlining. In Scala 3 there is transparent
.
The use case sounds like derivation or generation instead of inference.
Could we have e.g. an @infer
annotation for this?