具有逆差的隐式解决方案

Kev*_*ith 7 scala

给定父和子类.

scala> class Parent
defined class Parent

scala> class Child extends Parent
defined class Child
Run Code Online (Sandbox Code Playgroud)

定义父母和子女的含义

scala> implicit val a = new Parent
a: Parent = Parent@5902f207

scala> implicit val b = new Child
b: Child = Child@3f7d8bac
Run Code Online (Sandbox Code Playgroud)

使用implicitly找出其中隐含的得到解决.

scala> implicitly[Child] 
res1: Child = Child@3f7d8bac
Run Code Online (Sandbox Code Playgroud)

我理解的例证:

         Parent
           |
         Child -- implicit resolution gets the most specific, lowest sub-type
Run Code Online (Sandbox Code Playgroud)

现在,让我们使用逆变型.

scala> trait A[-T]
defined trait A

scala> case class Concrete[T]() extends A[T]
defined class Concrete
Run Code Online (Sandbox Code Playgroud)

然后定义Parent和Child类.

scala> class Parent
defined class Parent

scala> class Kid extends Parent
defined class Kid
Run Code Online (Sandbox Code Playgroud)

也为他们创造暗示.

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]]
                        ^
Run Code Online (Sandbox Code Playgroud)

在第一个例子中(没有逆变),Scala能够解析隐式Childfor implicitly[Parent].在我看来,它正在挑选最低的子类型.

但是,使用时contravariance,行为会发生变化.为什么?

som*_*ytt 4

你的隐式是类型化的Concrete,并且在这里是不变的。

尝试一下

case class Concrete[-T]() extends A[T]
Run Code Online (Sandbox Code Playgroud)

或者

implicit val x: A[Parent] = Concrete[Parent]
Run Code Online (Sandbox Code Playgroud)

更多的话:

隐式(值或视图)应该具有显式类型,这样您就不会对推断的类型感到惊讶。选择隐式完全取决于类型。

它使用与用于选择重载符号的替代项的重载解析转换相同的规则来选择隐式之一。

对于简单值(不是函数调用),这取决于一致性或子类型化。

还有一条规则是“派生类型”(通常是子类)中的定义是首选。

您可以仅使用常用的家用材料进行以下测试:

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
Run Code Online (Sandbox Code Playgroud)

编辑:

关于为什么测试类型很重要的另一种观点:

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]]
                        ^
Run Code Online (Sandbox Code Playgroud)

给它们明确的类型,具体的差异就不重要了。您总是为隐式类型提供显式类型,对吗?就像逆名告诉我们的那样?

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)
Run Code Online (Sandbox Code Playgroud)

  • 我添加了更多单词,简化了规范的 6.26.3。(简化一下,连我也能理解。) (2认同)