I am designing an API using type classes in some cases however I have encountered a problem with implicit resolution. As shown below, if there is an implicit object for type A but an object of type B extends A
is passed to the method, then an implicit object cannot be found. Is there a way to make this work or do callers have to put implicit objects into scope for each subclass?
Here is an example:
class A
class B extends A
class T[+X]
object T {
implicit object TA extends T[A]
}
def call[X:T](x:X) = println(x)
// compiles
call(new A)
// doesn't compile
call(new B)
var a = new A
// compiles
call(a)
a = new B
// compiles
call(a)
val b = new B
// doesn't compile
call(b)
This fails to compile with the following output:
/private/tmp/tc.scala:16: error: could not find implicit value for evidence parameter of type this.T[this.B] call(new B) ^ /private/tmp/tc.scala:28: error: could not find implicit value for evidence parameter of type this.T[this.B] call(b)
The call call(new B)
means call[B](new B)(tB)
such that tb is of type T[B] or subclass of it. (A method that expects argument of type T can only expect T or subclass of T, e.g., def foo(s: String)
cannot be called with argument of type Any
). T[A] is not a subtype of T[B]
To fix, you can change T to be defined T[-X]
. This means that the compiler will consider T[A] to be a subtype of T[B]
The following works fine:
scala> def call[X](x: X)(implicit evidence: T[X]<:<T[X]) = println(x)
call: [X](x: X)(implicit evidence: <:<[T[X],T[X]])Unit
scala> call(new A)
line0$object$$iw$$iw$A@1d869b2
scala> call(new B)
line2$object$$iw$$iw$B@b3a5d1
scala> val b = new B
b: B = B@30e4a7
scala> call(b)
line2$object$$iw$$iw$B@30e4a7
In your case compilation fails, because def call[X:T](x:X) = println(x)
is treated as call: [X](x: X)(implicit evidence$1: T[X])Unit
. In order to pass the subtype, you may use generalized type constraints.
Try this:
object T {
implicit def TA[X <: A] = new T[X]
}
import T._
or simply:
implicit def TA[X <: A] = new T[X]
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