【问题标题】:scala: using method of extending class based on macros invokes trait default impl if type of instance is statically the base classscala:如果实例类型静态地是基类,则使用基于宏扩展类的方法调用特征默认impl
【发布时间】:2020-03-25 10:27:04
【问题描述】:

我有一个特质T, 我用 C 类中的宏实现它。 我创建了一个 C 实例并调用它的方法。 如果包含 C 实例的 val 的类型是 C - 按预期工作。 如果包含 C 实例的 val 的类型是 T - 像 T 一样调用方法。

我能想出的最好的描述方式是 scala-macros 中的“虚拟表损坏”,但我不知道这是否是一回事......

示例代码:

Type in expressions for evaluation. Or try :help.

scala>

scala>

scala> import language.experimental.macros
import language.experimental.macros

scala>

scala>

scala> trait T { def doSomething(): Unit = println ("trait") }
defined trait T

scala>

scala>

scala> import scala.reflect.macros.Context
import scala.reflect.macros.Context

scala>

scala>

scala> object Macro {
     |   def doSomething(c: Context)(): c.universe.Tree = {
     |     import c.universe._
     |     q"""println ("macro")"""
     |   }
     | }
warning: there was one deprecation warning (since 2.11.0); for details, enable `:setting -deprecation' or `:replay -deprecation'
defined object Macro

scala>

scala>

scala> class C extends T { override def doSomething(): Unit = macro Macro.doSomething }
defined class C

scala>

scala> val c: C = new C()
c: C = C@3bd1883a

scala> c.doSomething()
macro

scala>

scala> val t: T = new C()
t: T = C@4079fec7

scala> t.doSomething()
trait

【问题讨论】:

  • 我的实际用例是为 scala-logging 贡献一个 PR,我希望通过 2 个不同的宏实现来实现基本特征“ALogger”,并希望记录器的消费者能够抽象地说他们有 ALogger,但是记录器稍后通过 Mixin 或继承来确定。 github.com/lightbend/scala-logging/pull/212

标签: scala scala-macros


【解决方案1】:

Def 宏在编译时扩展,因此后期绑定/动态调度(这是一个运行时特性)对它们来说是不可能的。

在编译时不知道t 的类型为C,在编译时只知道t 的类型为T

在此处查看详细信息: 尤金·布尔马科。 Scala 中编译时和运行时元编程的统一 https://infoscience.epfl.ch/record/226166/files/EPFL_TH7159.pdf 页。 98, §4.6.1 “继承”

【讨论】:

  • 也许很遗憾宏以这种方式幼稚。也许这可能会以某种方式在内部由一个生成的瘦包装器特征进行包装,该包装器特征以正常可预测的方式运行,并具有扩展宏的实现。我不知道这会产生什么影响,但这很有趣。
  • @nathang 抱歉,我不明白“生成的瘦包装器特征”(无论它是什么意思)有什么帮助。在运行时foo() 中的x.foo() 是根据x 的运行时类选择的。此信息在编译时尚不清楚。您可以将编译时的信息持久保存到运行时(类型标签、美味、语义数据库)。但是为了在编译时使用运行时信息,您需要时间机器。或者您将不得不对标准工作流程进行大量更改:第一个编译时间,第一个运行时(持久化信息),第二个编译时间(使用持久化的信息),第二个运行时。
猜你喜欢
  • 1970-01-01
  • 2015-09-22
  • 1970-01-01
  • 1970-01-01
  • 2012-05-24
  • 2019-04-21
  • 2015-07-14
  • 1970-01-01
  • 2011-11-10
相关资源
最近更新 更多