Ran into this strange behavior when changed upper bound in the implementation, but forgot to change it in the interface. I think last statement should not compile, but it does and returns unexpected result.
trait SuperBase
trait Base extends SuperBase
class SuperBaseImpl extends SuperBase
trait Service {
def doWork[T <: Base : Manifest](body: T => Unit): String
def print[T <: Base : Manifest]: String
}
object ServiceImpl extends Service {
override def doWork[T <: SuperBase : Manifest](body: T => Unit): String =
print[T]
def print[T <: SuperBase : Manifest]: String =
manifest[T].runtimeClass.toString
}
val s: Service = ServiceImpl
// does not compile as expected
// s.print[SuperBaseImpl]
// returns "interface Base"
s.doWork { x: SuperBaseImpl => () }
Edit
As @som-snytt mentioned with -Xprint:typer
option we can see what compiler actually infers:
s.doWork[Base with SuperBaseImpl]
This explains why we are getting "interface Base". But I still not quite understand how and why type inference work in this case.
With -Xprint:typer
, you'll see what the compiler infers for T
:
s.doWork[Base with SuperBaseImpl]
What is the bound trying to express? Functions are co-variant in the parameter, so you are expressing that body
must accept a certain arg of a sufficiently narrow type. Normally, you require that a function must deal with a wide type.
Maybe you intended a lower bound.
scala> trait SuperBase
defined trait SuperBase
scala> trait Base extends SuperBase
defined trait Base
scala> class SuperBaseImpl extends SuperBase
defined class SuperBaseImpl
scala> trait Service { def f[A >: Base : Manifest](g: A => Unit): String }
defined trait Service
scala> object Impl extends Service { def f[A >: Base : Manifest](g: A => Unit) = manifest[A].runtimeClass.toString }
defined object Impl
scala> (Impl: Service).f { x: Base => () }
res0: String = interface Base
scala> (Impl: Service).f { x: SuperBase => () }
res1: String = interface SuperBase
scala> (Impl: Service).f { x: SuperBaseImpl => () }
<console>:17: error: inferred type arguments [SuperBaseImpl] do not conform to method f's type parameter bounds [A >: Base]
(Impl: Service).f { x: SuperBaseImpl => () }
^
<console>:17: error: type mismatch;
found : SuperBaseImpl => Unit
required: A => Unit
(Impl: Service).f { x: SuperBaseImpl => () }
^
<console>:17: error: No Manifest available for A.
(Impl: Service).f { x: SuperBaseImpl => () }
^
scala> object Impl extends Service { def f[A >: SuperBase : Manifest](g: A => Unit) = manifest[A].runtimeClass.toString }
<console>:14: error: overriding method f in trait Service of type [A >: Base](g: A => Unit)(implicit evidence$1: Manifest[A])String;
method f has incompatible type
object Impl extends Service { def f[A >: SuperBase : Manifest](g: A => Unit) = manifest[A].runtimeClass.toString }
^
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With