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 class. For both classes, a particular method is available (via implicit evidence) only if one of the two traits underlies the type parameter. This compiles fine:
trait Foo
trait ReadableFoo extends Foo {def field: Int}
case class Bar[+F <: Foo](foo: F) {
def readField(implicit evidence: F <:< ReadableFoo) = foo.field
}
case class Grill[+F <: Foo, +B <: Bar[F]](bar: B) {
def readField(implicit evidence: F <:< ReadableFoo) = bar.readField
}
However, since Bar is covariant in F, I shouldn’t need the F parameter in Grill. I should just require that B is a subtype of Bar[ReadableFoo]. This, however, fails:
case class Grill[+B <: Bar[_]](bar: B) {
def readField(implicit evidence: B <:< Bar[ReadableFoo]) = bar.readField
}
with the error:
error: Cannot prove that Any <:< this.ReadableFoo.
def readField(implicit evidence: B <:< Bar[ReadableFoo]) = bar.readField
Why is the implicit evidence not being taken into account?
The call
bar.readFieldis possible because the evidence instance<:<allows an implicit conversion fromBtoBar[ReadableFoo].The problem I think that to call
readFieldyou need a successive evidence parameterF <:< ReadableFoo. So my guess is, the compiler doesn’t fully substitute the type parameter ofBarin the first search stage of the implicit resolution (because to findreadField, it just requires anyBarin 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
applymethod of<:<, or by using the helper methodimplicitly: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: