简体   繁体   English

Scala - 具有下限的通用隐式

[英]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.工作得很好。

--- Old answer - still relevant but approaches from different perspective -- --- 旧答案 - 仍然相关,但从不同的角度进行处理 -

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.解决方案原来是通过像这样更改它们的定义来将参数Bf移动到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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM