Scala编译器如何使用`<:<`来合成隐式证据?

时间:2016-03-16 12:30:25

标签: scala implicits

鉴于Scala中的这个(公认的设计)代码片段:

object Main extends App {

  class X { def foo = 1 }

  def f[A](value: A)(implicit ev: A <:< X) = { value.foo }

  println(f(new X()))

}

Scala编译器做了什么来传递这个?我查看了Predef中的一些代码,但我不了解实现。请详细说明一步。

1 个答案:

答案 0 :(得分:6)

调用点

让我们看一下你写的时推文类型的作用:

f(new X())

首先要弄清楚A的模板参数f是什么。 Scala中的类型推断在参数列表中从左到右,因此非常简单(给定new X的类型为X),我们得到

f[X](new X)

现在编译器需要找到X <:< X类型的隐式值(记住,A已解析为X)。

要查找隐式值,编译器会查看各个位置,其中包括您当前的范围(导入Predef._)。

然后编译器找到Predef.$conforms

implicit def $conforms[A]: A <:< A = // some implementation

因此,可以使用X <:< X作为参数调用它来生成X

f[X](new X)(Predef.$conforms[X])

就类型检查而言,$conforms的实际实现并不重要。

方法实施

现在让我们看一下实现:

 def f[A](value: A)(implicit ev: A <:< X) = { value.foo }

值为A类型(因此未知)。您想在foo上致电value。由于未在foo上定义A,因此编译器正在寻找将A转换为具有foo的内容的隐式函数(或方法)。

范围内有这样的事情:evA <:< B延伸A => B)。

因此,编译器使用ev

插入隐式转换
ev(value).foo

关于差异的小注释

您可能已经注意到,<:<在其参数中是变体:<:<[-From, +To]。这可用于生成实际的子类型证据。考虑:

class A
class B extends A

val ev1: A <:< A = conforms
val ev2: B <:< A = ev1 // makes sense, works because of variance

// Also
val ev3: B <:< B = conforms
val ev4: B <:< A = ev3 // makes sense, works because of variance

这是值得注意的原因,为什么不需要具有两个类型参数的conforms方法。此外,请注意,对于=:=,此行为特别(因为这是类型等效),因此它是不变的。