It seems empowering that one can effectively make assertions about legal return values of implementing methods just based on the type of the abstract function/method. I intuitively feel (most of) the compiler behaviour below makes sense but I would appreciate a clear explanation of why I should be able to assert that
def f[T](t: T): T
can only be the identity function (except that class E compiles too). I can appreciate that we know nothing about T as it is not bounded, but there are gaps in that explanation. The compiler reporting "found scala.Int(42) required Int" is not getting me closer to the light.
trait A{ def f[T](t: T): T }
// compiles
class B extends A{ override def f[Int](t: Int): Int = t }
// does not compile
class C extends A{ override def f[Int](t: Int): Int = t + 1 }
// does not compile
class D extends A{ override def f[Int](t: Int): Int = 42 }
// compiles
class E extends A{ override def f[Int](t: Int): Int = 42.asInstanceOf[Int] }
// compiles
class F extends A{ override def f[Int](t: Int): Int = identity(t) }
The problem in your examples is that the Int
in your examples is not the normal 32-bit integer type (which is scala.Int
); instead, you have a type parameter that happens to be named Int
. That's confusing you: you think your Int
is scala.Int
, but it isn't, it's a type parameter with a confusing name.
So, for example this:
class C extends A{ override def f[Int](t: Int): Int = t + 1 }
Does not mean you are defining a method that takes a scala.Int
; you're defining a method with a type parameter that has the name Int
. You could give it any other name, for example X
, then it would be exactly the same:
class C extends A{ override def f[X](t: X): X = t + 1 }
It doesn't compile because there are no constraints on the type parameter, so the compiler doesn't know that the type has a +
method.