Consider the following:
class Super
class Sub extends Super
implicit val implicitOption: Option[Super] = None
def f[A, B >: A](a: A)(implicit i: Option[B]) = println("It worked")
If I call f(new Super)
, it works fine, but f(new Sub)
gives me a compilation error (could not find implicit value for parameter i
).
Why can't implicitOption
be used as the implicit parameter when A = Sub
?
In this case, compiler needs some help figuring out which B
to use. So
f[Sub, Super](new Sub)
works just fine.
Because Option
is covariant - which means you can pass Option[Sub]
where Option[Super]
is expected, but not the vice versa.
To make this work, you'd need a contravariant class (let's call it ContraOption
for now) - so that you can pass ContraOption[Super]
where ContraOption[Sub]
is expected.
Continuing on your example:
class Super
class Sub extends Super
implicit val implicitOption: Option[Super] = None
sealed abstract class ContraOption[-A]
case object ContraNone extends ContraOption[Any]
case class ContraSome[A](value: A) extends ContraOption[A]
implicit val implicitContraOption: ContraOption[Super] = ContraNone
def f[A, B >: A](a: A)(implicit i: Option[B]) = println("It worked")
def f2[A, B >: A](a: A)(implicit i: ContraOption[B]) = println("It worked 2")
f(new Super) // It worked
f2(new Sub) // It worked 2
Note that both trying to do f(new Sub)
(as in your example) and f2(new Super)
fails compilation with "implicit not found".
For more fundamental understanding of co- and contravariance, please refer to the docs . But simply put, covariant generic class "follows" the ancestor-descendent links, while contravariant classes reverse the direction. To illustrate (in pseudo-scala):
class Super
class Sub extends Super
class Covariant[+A]
class Contravariant[-A]
This gives us the following relationships:
Sub <: Super
Covariant[Sub] <: Covariant[Super]
Contravariant[Super] <: Covariant[Sub]
The solution turned out to be to move the parameter B
from f
to implicitOption
by changing their definitions like this.
implicit def implicitOption[B <: Super]: Option[B] = None
def f[A](a: A)(implicit i: Option[A]) = println("It worked")
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.