鉴于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
中的一些代码,但我不了解实现。请详细说明一步。
答案 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
的内容的隐式函数(或方法)。
范围内有这样的事情:ev
(A <:< 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
方法。此外,请注意,对于=:=
,此行为特别不(因为这是类型等效),因此它是不变的。