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 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
(因此未知)。您想在 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
方法的明显原因。此外,请注意,这种行为是 而不是 =:=
想要的(因为这是类型等价),所以它是不变的。
鉴于 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 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
(因此未知)。您想在 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
方法的明显原因。此外,请注意,这种行为是 而不是 =:=
想要的(因为这是类型等价),所以它是不变的。