隐式方法参数解析?斯卡拉编译器?

Implicit methods argument resolution? Scala compiler?

我正在 Scala 中学习隐式方法。例如考虑以下测试代码:

object Test{
  def main(args: Array[String]) = {
    implicit val f: Int => String = (_: Int).toString + "sdgfdfsg"
    val Ext(i) = 10
    println(i)
  }
}


object Ext{
  def unapply(i: Int)(implicit f: Int => String): Option[String] = Some(f(i))
}

此代码按预期工作。但我不清楚的是它为什么有效。所以我阅读了 Scala compiler phases 并发现所有脱糖都是在第一阶段执行的:

parser   1  parse source into ASTs, perform simple desugaring

但是脱糖的顺序是什么?在示例中似乎

val Ext(i) = 10

首先被脱糖为

val i = Ext.unapply(10)

然后编译器找到了隐式值implicit val f: Int => String = (_: Int).toString + "sdgfdfsg",最终的脱糖版本是

val i = Ext.unapply(10)(f)

为什么隐式函数稍后会被脱糖?或者它是如何工作的?

隐式不是 "desugared"。 "desugar" 一词源于表达式 "syntactic sugar"。注意 "syntactic sugar" 中的 "syntactic" 部分。这意味着这种转换可以发生在非常低​​的句法层面。比如为了脱糖

val Ext(i) = ten

变成类似

的东西
val i = Ext.unapply(ten).get

您不必了解 Extteni 的类型。您甚至根本不必知道这些符号是否存在于范围内。您获取原始句法片段,然后稍微重新排序,仅此而已。

(我添加了 get 因为否则类型不匹配,scala -print 打印的实际脱糖代码更加可怕)


implicit 的搜索非常不同。它关键取决于以下事实:所有符号和方法签名都已解析,并且所有子表达式的所有类型都已派生 [脚注 1]。这是因为:

  • 必须解析符号,否则编译器甚至不知道对象 Ext 上的方法 unapply 需要额外的隐式参数。
  • 必须知道类型,否则它不知道要搜索什么类型的隐式。

这两种信息都不存在于程序的原始句法结构中,必须在后期阶段首先重构。这就是为什么隐式解析必须在纯句法脱糖之后。

[脚注 1] 过于简单化了:类型检查器必须在插入一些隐式后以某种方式推断出更多信息,因此这些阶段似乎必须交织在一起。