Here is a simple setup with two traits, a class with a covariant type parameter bounded by the previous traits, and a second class with a type parameter bounded by the other cla
The call bar.readField
is possible because the evidence instance <:<
allows an implicit conversion from B
to Bar[ReadableFoo]
.
The problem I think that to call readField
you need a successive evidence parameter F <:< ReadableFoo
. So my guess is, the compiler doesn't fully substitute the type parameter of Bar
in the first search stage of the implicit resolution (because to find readField
, it just requires any Bar
in the first place). And then it chokes on the second implicit resolution, because there is no form of 'backtracking' as far as I know.
Anyway. The good thing is, you know more than the compiler and you can engage the conversion explicitly, either by using the apply
method of <:<
, or by using the helper method implicitly
:
case class Grill[+B <: Bar[_]](bar: B) {
def readField(implicit evidence: B <:< Bar[ReadableFoo]) = evidence(bar).readField
}
case class Grill[+B <: Bar[_]](bar: B) {
def readField(implicit evidence: B <:< Bar[ReadableFoo]) =
implicitly[Bar[ReadableFoo]](bar).readField
}
There is another possibility which might be the cleanest, as it doesn't rely on the implementation of <:<
which might be a problem as @Kaito suggests:
case class Grill[+B <: Bar[_]](bar: B) {
def readField(implicit evidence: B <:< Bar[ReadableFoo]) =
(bar: Bar[ReadableFoo]).readField
}