Scala - 具有下限的通用隐式
Scala - Generic implicit with lower bound
考虑以下几点:
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")
如果我调用 f(new Super)
,它工作正常,但是 f(new Sub)
给我一个编译错误(找不到参数 i
的隐式值)。
为什么implicitOption
在A = Sub
时不能作为隐式参数?
在这种情况下,编译器需要一些帮助来确定要使用哪个 B
。所以
f[Sub, Super](new Sub)
工作正常。
--- 旧答案 - 仍然相关,但从不同的角度出发 --
因为 Option
是协变的 - 这意味着您可以在需要 Option[Super]
的地方传递 Option[Sub]
,但反之则不行。
要完成这项工作,您需要一个逆变 class(我们暂时称它为 ContraOption
)- 这样您就可以将 ContraOption[Super]
传递给 ContraOption[Sub]
预计。
继续你的例子:
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
请注意,尝试执行 f(new Sub)
(如您的示例)和 f2(new Super)
都无法通过 "implicit not found" 进行编译。
关于协变和逆变的更基本的理解,请参考docs。但简单地说,协变泛型 class "follows" 祖先-后代链接,而逆变 classes 反向。为了说明(在伪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]
解决方案原来是将参数 B
从 f
移动到 implicitOption
,方法是像这样更改它们的定义。
implicit def implicitOption[B <: Super]: Option[B] = None
def f[A](a: A)(implicit i: Option[A]) = println("It worked")
考虑以下几点:
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")
如果我调用 f(new Super)
,它工作正常,但是 f(new Sub)
给我一个编译错误(找不到参数 i
的隐式值)。
为什么implicitOption
在A = Sub
时不能作为隐式参数?
在这种情况下,编译器需要一些帮助来确定要使用哪个 B
。所以
f[Sub, Super](new Sub)
工作正常。
--- 旧答案 - 仍然相关,但从不同的角度出发 --
因为 Option
是协变的 - 这意味着您可以在需要 Option[Super]
的地方传递 Option[Sub]
,但反之则不行。
要完成这项工作,您需要一个逆变 class(我们暂时称它为 ContraOption
)- 这样您就可以将 ContraOption[Super]
传递给 ContraOption[Sub]
预计。
继续你的例子:
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
请注意,尝试执行 f(new Sub)
(如您的示例)和 f2(new Super)
都无法通过 "implicit not found" 进行编译。
关于协变和逆变的更基本的理解,请参考docs。但简单地说,协变泛型 class "follows" 祖先-后代链接,而逆变 classes 反向。为了说明(在伪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]
解决方案原来是将参数 B
从 f
移动到 implicitOption
,方法是像这样更改它们的定义。
implicit def implicitOption[B <: Super]: Option[B] = None
def f[A](a: A)(implicit i: Option[A]) = println("It worked")