【问题标题】:How does the Scala compiler synthesize implicit evidence with `<:<`?Scala 编译器如何用 `<:<` 合成隐含证据?
【发布时间】:2016-03-16 21:44:58
【问题描述】:

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

【问题讨论】:

标签: scala implicits


【解决方案1】:

呼叫站点

让我们看看类型推断器在你写的时候做了什么:

f(new X())

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

f[X](new X)

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

为了查找隐式值,编译器会在不同的地方查找,其中包括您当前的范围(在其中导入了Predef._)。

然后编译器找到Predef.$conforms:

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

所以这可以用来产生一个X &lt;:&lt; 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 的内容的隐式函数(或方法)。

范围内有这样一个东西:ev (A &lt;:&lt; B extends A =&gt; B)。

因此,编译器使用ev 插入隐式转换:

ev(value).foo

关于方差的小提示

您可能已经注意到,&lt;:&lt; 的参数变体:&lt;:&lt;[-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 方法的明显原因。此外,请注意,=:= 特别需要这种行为(因为这是类型等价的),所以它是不变的。

【讨论】:

  • gzm0 非常感谢。还有一个问题,什么时候需要 <:>
  • 添加了一小部分。
猜你喜欢
  • 1970-01-01
  • 2014-03-16
  • 2019-09-04
  • 1970-01-01
  • 1970-01-01
  • 2015-08-22
  • 2013-09-14
  • 1970-01-01
  • 2011-08-24
相关资源
最近更新 更多