2

在 scala 2 中已知宏是严格本地的,并且只在定义类时执行一次。当与抽象类型结合时,这一特性似乎特别弱,因为将抽象类型转换为具体类型的过程通常会绕过宏并使用其自己的原始规则。

下面的测试代码中展示了一个反直觉结果的简单示例:

  trait BB {

    def ttag = implicitly[TypeTag[this.type]]
  }

  case class AA() extends BB

  it("can TypeTag") {

    val kk = AA()

    TypeViz(kk.ttag).peek // this function visualise the full type tree of the type tag
  }

如果执行,kk的类型变成:

-+ BB.this.type
 !-+ InfoCTSpec.this.BB
   !-+ Object
     !-- Any

糟糕,类型 AA 完全被忽略了,因为implicitly[TypeTag[this.type]]它由内置宏隐式支持,该宏仅在定义 BB 时执行一次,而不是在定义 AA 并具体化实际时执行kk.this.type。我发现它非常笨拙并且容易导致一些其他特性(例如模式匹配、类型 lambda)由于运行时类型擦除而降级。

我想编写/使用语言扩展,例如制作TypeTag[this.type]AA 的子类型,而不引入运行时开销和超出范围的上下文对象(因此,没有隐含)。我怎样才能以最少的黑客攻击做到这一点?我对编译器扩展和宏等非常核心的解决方案持开放态度,但显然首选可以顺利转移到 scala 3/dotty 的优雅解决方案。

PS 似乎有点“内联/编译时”功能已经部分实现了我的设想。这是正确的印象吗?

4

1 回答 1

0

您可以自由编写宏和编译器插件,但将隐式解析从定义站点推迟到调用站点的常规机制是implicitly用隐式参数替换

trait BB {
  def ttag(implicit tt: TypeTag[this.type]) = tt
}

当使用类型参数进行隐式解析时,为什么 val 位置很重要?

不引入运行时开销和超出范围的上下文对象(因此,没有隐含)。

目前尚不清楚为什么要避免隐式。隐式在编译时解析。如果你用宏或编译器插件替换它,那么无论如何你都会在编译时手动解析隐式。

于 2021-06-20T13:16:03.870 回答