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

How does the Scala compiler synthesize implicit evidence with `<:<`?

鉴于 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 中的一些代码,但我不明白其实现。请给出详细的步骤说明。

调用站点

让我们看看类型推断器在您编写时做了什么:

f(new X())

首先要搞清楚,f的模板参数A是什么。 Scala 中的类型推断在参数列表中从左到右,所以很简单(给定 new XX 类型),我们得到

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(因此未知)。您想在 value 上致电 foo。由于 foo 未在 A 上定义,编译器正在寻找将 A 转换为具有 foo.[=45= 的隐式函数(或方法) ]

范围内有这样的东西:ev (A <:< B extends 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 方法的明显原因。此外,请注意,这种行为是 而不是 =:= 想要的(因为这是类型等价),所以它是不变的。