繁体   English   中英

后代类中的宏扩展

[英]Macro expansion in descendant classes

我有下一个类结构:

trait SomeType

trait Root {
  val allMySomeTypes: Seq[SomeType]
}

class Child extends Root {
  object MyType1 extends SomeType {...}
  object MyType2 extends SomeType {...}
}

我想初始化val allMySomeTypes作为扩展在具体类中定义的SomeType的所有对象的Seq。 所以对于Child实例,它将是val allMySomeTypes:Seq [SomeType] = Seq(MyType1,MyType2)。

我写了一个宏来查找具有一些基类型的对象:

def getMembersOfCurrentByType_impl[S](c: Context)(implicit ev: c.WeakTypeTag[S]) = {
  import c.universe._
  val seqApply = Select(reify(Seq).tree, newTermName("apply"))
  val objs = c.enclosingClass.symbol.typeSignature.declarations.collect {
    case o: ModuleSymbol if o.typeSignature <:< ev.tpe => Ident(o) //Select(c.prefix.tree, o)
  }.toList
  c.Expr[Seq[S]] {Apply(seqApply, objs)}
}

并绑定到

trait Root {
  val allMySomeTypes: Seq[SomeType] = macro getMembersOfCurrentByType_impl[SomeType]
}

但是,显然,由于基本特征的宏扩展,我在Child类中有空序列。 我可以在没有在Child类中额外输入而不使用运行时反射的情况下构建实际成员的Seq吗?

子类检测的一般情况

有趣的是,几天前我们在scala-user上进行了类似的讨论 ,在那里我们讨论了在一般情况下列出给定类的所有子类的可能性。 这是我当时发布的答案:

目前不可能枚举任意类的所有子类,但我们确实有一个名为ClassSymbol.knownDirectSubclasses的API,它枚举了密封的后代。

但不幸的是,即便这样也不容易。 knownDirectSubclasses API在人们以某种​​方式使用它的意义上或多或少都很好,但它也有一个严重的缺陷,我们目前不知道如何修复: https//groups.google.com/forum/#! topic / scala-internals / LRfKffsPxVA

子类检测的特殊情况

但是,这个StackOverflow问题会询问更具体的内容。 我们如何将自己局限于某个类的成员,然后可以选择那些我们感兴趣的类的子类吗?

好吧,事实证明,尽管目前有一个API来处理它( c.enclosingClass系列的c.enclosingTree ),即使这个特殊情况仍然无法稳健地处理。

问题是Scala宏在类型检查期间被扩展,这意味着在给定宏被扩展时,它的封闭树被强调了。 “被类型检查”状态意味着一些封闭的树可能暂时处于不一致的状态,如果试图检查它们就会崩溃。 Scala Macro Annotations上有一个更详细的讨论:带注释类型的c.TypeCheck会导致StackOverflowError ,但在这里我只举一个简单的例子。

例如,如果您的宏在具有未指定返回类型的方法内扩展(例如def foo = yourMacro(1, 2, 3) ),那么调用c.enclosingDef.symbol.typeSignature将失败宏扩展,因为,以及,要知道方法的签名,你需要推断它的返回类型,但要推断你需要扩展宏的返回类型,你需要知道方法的签名等。顺便说一下,编译器为了不在这里无限循环,它足够聪明 - 它会提前打破循环并显示循环引用错误。

这意味着要很好地处理非局部宏扩展,我们需要对类型签名及其依赖关系进行某种声明性抽象,而不仅仅是命令式typeSignature方法。 在过去的几个月里,我一直在思考这个问题,但到目前为止还没有得到任何令人满意的结果,这也是为什么在Scala 2.11中我们要弃用非本地c.enclosingTree的原因之一c.enclosingTree方法: https//github.com/scala/scala/pull/3354

随着宏观注释的出现,这个主题将变得更加重要,所以我们还没有承认失败。 我认为,期待这一领域取得进展是合理的,但我们没有具体的日期可以提供给这一领域。

一种可能的解决方法

正如我上面提到的,我们在检测子类的任务中遇到的“唯一”问题是它是一个非执行它的宏扩展操作。 那么我们如何将其转变为本地运营呢? 事实证明这是可能的。

通过在Child类上放置宏注释,在宏中,您将能够在类型检查之前看到类的所有成员,这意味着检查这些成员不会导致潜在的虚假循环错误。

但是,如果这些成员没有受到严格监控,那么他们对我们有什么好处呢? 我们需要类型来执行子类检查,对吧? 嗯,是的,不。 我们确实需要类型,但我们不必从成员本身获取这些类型。 我们可以使用带注释的类,复制它,对其进行检查,然后检查类型检查的结果,而不必担心会破坏被注意者。 这是一个为实施此策略提供灵感的示例: 在处理宏注释时无法访问Parent的成员

TL;博士

  1. 目前,由于理论和实施原因,宏观扩张期间的非本地运营可能很难或不可能稳健地执行。 SI-7046就是一个例子,这个问题提供了另一个例子。
  2. 有时,通过在宏观中包含更大的范围,可以将非本地宏观扩张问题重新定位为本地宏观扩张问题。 宏天堂插件提供的宏注释可能有助于此目的。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM