Search code examples
scalamacrosscala-macrosscala-reflectscala-2.13

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


The following code snippet is a short scala macro bundle definition from a thoughtworks project:

  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]"

    }

  }

(courtesy of https://github.com/ThoughtWorksInc/feature.scala/blob/4d19cc19016d85f26925895f43f618e1b7552d09/SelfType/src/main/scala/com/thoughtworks/feature/SelfType.scala)

The last line as a quasiquote seems to contain a lot of boilerplate text:

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

Assuming that this macro bundle is defined inside a trait as part of a family polymorphism design pattern: there is no deterministic q"_root_.com.thoughtworks.feature.SelfType.make[$a, $out]", it has to be derived from an object variable vvv of the macro bundle when it is being compiled. How do I use this variable to make the quasiquote shorter and more adaptive?

There may be multiple methods to achieve this (e.g. for each implementation, define a Liftable for SelfType object). But that's even more boilerplate. I'm looking for the shortest solution. Ideally, something like this:

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

Solution

  • If you have a static reference (e.g. the type/companion is imported), you can do:

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

    You can also use symbolOf[A].companion if you have A: WeakTypeTag but no info about its companion. That might not work if the compiler doesn't consider the object A a companion to class A