scalatypes

return value "constrained" by function type in Scala


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) }

Solution

  • 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.