隐式分辨与逆变
给定父类和子类。
scala> class Parent
defined class Parent
scala> class Child extends Parent
defined class Child
定义Parent和Child的含义
scala> implicit val a = new Parent
a: Parent = Parent@5902f207
scala> implicit val b = new Child
b: Child = Child@3f7d8bac
implicitly
使用来找出哪些隐式被解析。
scala> implicitly[Child]
res1: Child = Child@3f7d8bac
我的理解的插图:
Parent
|
Child -- implicit resolution gets the most specific, lowest sub-type
现在,让我们使用逆变类型。
scala> trait A[-T]
defined trait A
scala> case class Concrete[T]() extends A[T]
defined class Concrete
然后定义一个Parent和Child类。
scala> class Parent
defined class Parent
scala> class Kid extends Parent
defined class Kid
也为他们创建暗示。
scala> implicit val x = Concrete[Parent]
x: Concrete[Parent] = Concrete()
scala> implicit val y = Concrete[Kid]
y: Concrete[Kid] = Concrete()
scala> implicitly[A[Parent]]
res1: A[Parent] = Concrete()
scala> implicitly[A[Kid]]
<console>:21: error: ambiguous implicit values:
both value x of type => Concrete[Parent]
and value y of type => Concrete[Kid]
match expected type A[Kid]
implicitly[A[Kid]]
^
在第一个例子(没有逆变),斯卡拉是能够解决的隐含Child
的implicitly[Parent]
。 在我看来,它选择最低的子类型。
但是,使用contravariance
,行为会发生变化。 为什么?
你的含义是键入Concrete
,这是不变的。
试一试
case class Concrete[-T]() extends A[T]
要么
implicit val x: A[Parent] = Concrete[Parent]
更多词语:
Implicits(值或视图)应该有一个明确的类型,所以你从不会对推断的类型感到惊讶。 隐含的选择全是关于类型的。
它使用与重载解析转换相同的规则来选择一个隐含条件,用于选择重载符号的替代选项。
对于简单的值(不是函数调用),归结为一致性或子类型。
还有一个规则是“派生类型”(通常是一个子类)中的定义是首选。
以下是您只能使用常用家用材料进行的测试:
scala> :power
** Power User mode enabled - BEEP WHIR GYVE **
** :phase has been set to 'typer'. **
** scala.tools.nsc._ has been imported **
** global._, definitions._ also imported **
** Try :help, :vals, power.<tab> **
scala> trait A[-T]
defined trait A
scala> case class Concrete[T](i: Int) extends A[T]
defined class Concrete
scala> class Parent ; class Kid extends Parent
defined class Parent
defined class Kid
// it will pick X if X isAsSpecific as Y but not conversely
scala> typer.infer.isAsSpecific(typeOf[Concrete[Kid]],typeOf[Concrete[Parent]])
res0: Boolean = false
scala> typer.infer.isAsSpecific(typeOf[Concrete[Parent]],typeOf[Concrete[Kid]])
res1: Boolean = false
scala> case class Concrete[-T](i: Int) extends A[T]
defined class Concrete
scala> typer.infer.isAsSpecific(typeOf[Concrete[Kid]],typeOf[Concrete[Parent]])
res2: Boolean = false
scala> typer.infer.isAsSpecific(typeOf[Concrete[Parent]],typeOf[Concrete[Kid]])
res3: Boolean = true
编辑:
关于为什么它关系你正在测试什么类型的另一种观点:
scala> trait A[-T]
defined trait A
scala> case class Concrete[T](i: Int) extends A[T] // invariant
defined class Concrete
scala> class Parent ; class Kid extends Parent
defined class Parent
defined class Kid
scala> implicitly[Concrete[Parent] <:< Concrete[Kid]]
<console>:13: error: Cannot prove that Concrete[Parent] <:< Concrete[Kid].
implicitly[Concrete[Parent] <:< Concrete[Kid]]
^
scala> implicit val x: Concrete[Parent] = Concrete[Parent](3) // the inferred type
x: Concrete[Parent] = Concrete(3)
scala> implicit val y = Concrete[Kid](4)
y: Concrete[Kid] = Concrete(4)
// both values conform to A[Kid] (because A is contravariant)
// but when it puts x and y side-by-side to see which is more specific,
// it no longer cares that you were looking for an A. All it knows is
// that the values are Concrete. The same thing happens when you overload
// a method; if there are two candidates, it doesn't care what the expected
// type is at the call site or how many args you passed.
scala> implicitly[A[Kid]]
<console>:15: error: ambiguous implicit values:
both value x of type => Concrete[Parent]
and value y of type => Concrete[Kid]
match expected type A[Kid]
implicitly[A[Kid]]
^
给他们明确的类型,混凝土的方差并不重要。 你总是为你的暗示提供显式类型,对吧? 就像retronym告诉我们的?
scala> implicit val x: A[Parent] = Concrete[Parent](3)
x: A[Parent] = Concrete(3)
scala> implicit val y: A[Kid] = Concrete[Kid](4)
y: A[Kid] = Concrete(4)
scala> implicitly[A[Kid]]
res2: A[Kid] = Concrete(3)
链接地址: http://www.djcxy.com/p/77575.html