在 Scala 宏中,如何在编译时提升一个对象并在准引用中使用它?

In scala macro, how to lift an object and use it in quasiquote at compile time?

以下代码片段是来自 thoughtworks 项目的简短 Scala 宏包定义:

  private[SelfType] final class Macros(val c: whitebox.Context) {
    import c.universe._

    def apply[A: WeakTypeTag]: Tree = {
      val a = weakTypeOf[A]
      val selfTypes: List[Type] = {
        val selfTypeBuilder = List.newBuilder[Type]
        def buildSelfTypes(t: Type): Unit = {
          val dealiased = t.dealias
          dealiased match {
            case RefinedType(superTypes, refinedScope) =>
              superTypes.foreach(buildSelfTypes)
            case typeRef: TypeRef =>
              val symbol = dealiased.typeSymbol
              if (symbol.isClass) {
                selfTypeBuilder += symbol.asClass.selfType.asSeenFrom(dealiased, symbol)
              }
            case _ =>
          }
        }
        buildSelfTypes(a)
        selfTypeBuilder.result()
      }
      val out = selfTypes match {
        case Nil =>
          definitions.AnyTpe
        case _ =>
          internal.refinedType(selfTypes, c.internal.enclosingOwner)
      }
      q"_root_.com.thoughtworks.feature.SelfType.make[$a, $out]"

    }

  }

(由 https://github.com/ThoughtWorksInc/feature.scala/blob/4d19cc19016d85f26925895f43f618e1b7552d09/SelfType/src/main/scala/com/thoughtworks/feature/SelfType.scala 提供)

作为准引用的最后一行似乎包含很多样板文本:

q"_root_.com.thoughtworks.feature.SelfType.make[$a, $out]"

假设这个宏包是在特征内部定义的,作为家族多态设计模式的一部分:没有确定性q"_root_.com.thoughtworks.feature.SelfType.make[$a, $out]",它必须从对象变量vvv派生编译时的宏包。我如何使用此变量使准引用更短且更具适应性?

可能有多种方法可以实现这一点(例如,对于每个实现,为 SelfType 对象定义一个 Liftable)。但这更多的是样板文件。我正在寻找最短的解决方案。理想情况下,像这样:

val sym = Term(vvv)
q"$sym.make[$a, $out]"

找到我的第一个解决方案:

val name = SelfTypes.getClass.getCanonicalName.stripSuffix("$")
val tree = c.parse(name)
q"$tree.make[$a, $out]"

不确定它是否是最有效或最惯用的解决方案,我会让这个问题暂时搁置

如果你有一个静态引用(例如 type/companion 被导入),你可以这样做:

q"${symbolOf[SelfType.type]}.make[$a, $out]"

如果您有 A: WeakTypeTag 但没有关于其同伴的信息,您也可以使用 symbolOf[A].companion。如果编译器不认为 object Aclass A

的同伴,那可能行不通