[英]Scala - Generic implicit with lower bound
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
).如果我调用
f(new Super)
,它工作正常,但f(new Sub)
给我一个编译错误(找不到参数i
隐式值)。
Why can't implicitOption
be used as the implicit parameter when A = Sub
?当
A = Sub
时,为什么不能将implicitOption
用作隐式参数?
In this case, compiler needs some help figuring out which B
to use.在这种情况下,编译器需要一些帮助来确定使用哪个
B
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.因为
Option
是协变的 - 这意味着您可以在需要Option[Super]
地方传递Option[Sub]
,但反之则不然。
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.为了使这项工作,您需要一个逆变类(我们现在称其为
ContraOption
) - 这样您就可以在需要ContraOption[Sub]
地方传递ContraOption[Super]
。
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".请注意,尝试执行
f(new Sub)
(如在您的示例中)和f2(new Super)
无法通过“隐式未找到”进行编译。
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):
为了说明(在伪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.解决方案原来是通过像这样更改它们的定义来将参数
B
从f
移动到implicitOption
。
implicit def implicitOption[B <: Super]: Option[B] = None
def f[A](a: A)(implicit i: Option[A]) = println("It worked")
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.