Get the module symbol, given I have the module class, scala macro

后端 未结 1 1033
渐次进展
渐次进展 2021-01-28 11:02

I\'m trying to build a simple typeclass IsEnum[T] using a macro.

I use knownDirectSubclasses to get all the direct subclasses if T,

相关标签:
1条回答
  • 2021-01-28 11:23

    In 2.13 you can materialize scala.ValueOf

    val instanceTree = c.inferImplicitValue(appliedType(typeOf[ValueOf[_]].typeConstructor, subSymbol.asClass.toType))
    q"$instanceTree.value"
    

    Tree will be different

    sealed trait A
    object A {
      case object B extends A
      case object C extends A
    }
    
    //scalac: Seq(new scala.ValueOf(A.this.B).value, new scala.ValueOf(A.this.C).value)
    

    but at runtime it's still Seq(B, C).

    In 2.12 shapeless.Witness can be used instead of ValueOf

    val instanceTree = c.inferImplicitValue(appliedType(typeOf[Witness.Aux[_]].typeConstructor, subSymbol.asClass.toType))
    q"$instanceTree.value"
    
    //scalac: Seq(Witness.mkWitness[App.A.B.type](A.this.B.asInstanceOf[App.A.B.type]).value, Witness.mkWitness[App.A.C.type](A.this.C.asInstanceOf[App.A.C.type]).value)
    
    libraryDependencies += "com.chuusai" %% "shapeless" % "2.4.0-M1" // in 2.3.3 it doesn't work
    

    In Shapeless they use kind of

    subSymbol.asClass.toType match {
      case ref @ TypeRef(_, sym, _) if sym.isModuleClass => mkAttributedQualifier(ref)
    }
    

    https://github.com/milessabin/shapeless/blob/master/core/src/main/scala/shapeless/singletons.scala#L230

    or in our case simply

    mkAttributedQualifier(subSymbol.asClass.toType)
    

    but their mkAttributedQualifier also uses downcasting to compiler internals and the tree obtained is like Seq(A.this.B, A.this.C).

    Also

    Ident(subSymbol.companionSymbol)
    

    seems to work (tree is Seq(B, C)) but .companionSymbol is deprecated (in scaladocs it's written "may return unexpected results for module classes" i.e. for objects).

    Following approach similar to the one used by @MateuszKubuszok in his library enumz you can try also

    val objectName = symbol.fullName
    c.typecheck(c.parse(s"$objectName"))
    

    and the tree is Seq(App.A.B, App.A.C).

    Finally, if you're interested in the tree Seq(B, C) (and not some more complicated tree) it seems you can replace

    Ident(subSymbol.asInstanceOf[scala.reflect.internal.Symbols#Symbol].sourceModule.asInstanceOf[Symbol])
    

    with more conventional

    Ident(subSymbol.owner.info.decl(subSymbol.name.toTermName))
    
    0 讨论(0)
提交回复
热议问题