Reputation: 9521
Consider the following example:
sealed trait ST
object ST{
case class ST1() extends ST
case class ST2() extends ST
}
trait TypeClass[A]{
def doSome(a: A): Unit
}
case class Test[T](t: T)
implicit val tp: TypeClass[Test[_ <: ST]] = ??? //the implicit
def foo[A: TypeClass](a: A) = implicitly[TypeClass[A]].doSome(a)
val v: Test[_ <: ST] = ???
foo(v) //error: implicit not found
As can be seen the required implicit is in scope while it is not recognized by the compile.
Why does that happen and is there a workaround to call foo
?
Upvotes: 2
Views: 306
Reputation: 48400
Existential types are not inferred during implicit resolution so f(v)
fails because it is looking for an implicit value with non-inferred type
TypeClass[Test[_$4]]]
|
existential not inferred
however whey you explicitly provide the type variable instance foo[Test[_ <: ST]](v)
then implicit resolution should work because we are past inference stage.
It works in Scala 3 probably because internally it rewrites existential types to refined types.
Upvotes: 4
Reputation: 40500
If you change foo(v)
to foo(v)(tp)
it'll explain (a little bit) better why it doesn't want to use tp.
In a nutshell, def foo[A : TypeClass]
wants an implicit of type TypeClass[A]
.
When you do foo(v)
, A becomes Test[_ <: ST]
which means "Test
of some specific but unknown subtype of ST
". So, foo
wants an implicit for that specific type.
But tp
is not that. It is a "TypeClass for Test
or any subclass of ST
" (apparently _
means slightly different things in these two contexts, because v
is a concrete instance, that must have a specific type).
Long story short, Test[_ <: ST]
is not the actual type of v
, but a supertype of its type. So, to make it work, you just need to make the TypeClass
contravariant (TypeClass[-A]
) - that'll make foo
accept tp
as the implicit, because its type will be a subtype of what it expects.
Upvotes: 5