类型参数与WeakTypeTag反射+ quasiquoting不匹配(我想!)

时间:2015-06-10 16:38:36

标签: scala scala-macros scala-reflect

受到travisbrown的启发,我尝试使用宏来创建一些智能构造函数"。

鉴于

package mypkg
sealed trait Hello[A]
case class Ohayo[A,B](a: (A,B)) extends Hello[A]

val smartConstructors = FreeMacros.liftConstructors[Hello]

宏应该找到Hello的所有子类,查看它们的构造函数,并提取一些元素来为#34; smart构造函数填充此树":

q"""
  def $methodName[..$typeParams](...$paramLists): $baseType =
    $companionSymbol[..$typeArgs](...$argLists)
 """

我希望得到:

val smartConstructors = new {
  def ohayo[A, B](a: (A, B)): Hello[A] = Ohayo[A, B](a)
}

但是得到:

error: type mismatch;
 found   : (A(in class Ohayo), B(in class Ohayo))
 required: ((some other)A(in class Ohayo), (some other)B(in class Ohayo))
       val liftedConstructors = FreeMacros.liftConstructors[Hello]

乍一看,这棵树对我来说还不错:

scala> q" new { ..$wellTyped }"
res1: u.Tree =
{
  final class $anon extends scala.AnyRef {
    def <init>() = {
      super.<init>();
      ()
    };
    def ohayo[A, B](a: (A, B)): net.arya.constructors.Hello[A] = Ohayo[A, B](a)
  };
  new $anon()
}

但我猜它无形。如果我天真地尝试用info.typeParams.map(p => TypeName(p.name.toString))刷新typeParams,我得到&#34;不能将A拼接为类型参数&#34;当我做准备时。

我哪里错了?谢谢你看看。

-Arya

import scala.language.experimental.macros
import scala.reflect.api.Universe
import scala.reflect.macros.whitebox

class FreeMacros(val c: whitebox.Context) {
  import c.universe._
  import FreeMacros._


  def liftedImpl[F[_]](implicit t: c.WeakTypeTag[F[_]]): Tree = {
    val atc = t.tpe

    val childSymbols: Set[ClassSymbol] = subCaseClassSymbols(c.universe)(atc.typeSymbol.asClass)
    val wellTyped = childSymbols.map(ctorsForSymbol(c.universe)(atc)).unzip


    q"new { ..${wellTyped} }"
  }
}


object FreeMacros {
  def liftConstructors[F[_]]: Any = macro FreeMacros.liftedImpl[F]


  def smartName(name: String): String = (
    name.toList match {
      case h :: t => h.toLower :: t
      case Nil => Nil
    }
  ).mkString


  def subCaseClassSymbols(u: Universe)(root: u.ClassSymbol): Set[u.ClassSymbol] = {
    val subclasses = root.knownDirectSubclasses
    val cast = subclasses.map(_.asInstanceOf[u.ClassSymbol])
    val partitioned = mapped.partition(_.isCaseClass)
    partitioned match {
      case (caseClasses, regularClasses) => caseClasses ++ regularClasses.flatMap(r => subCaseClassSymbols(u)(r))
    }
  }


  def ctorsForSymbol(u: Universe)(atc: u.Type)(caseClass: u.ClassSymbol): (u.DefDef, u.DefDef) = {
    import u._
    import internal._

// these didn't help
//    def clearTypeSymbol(s: Symbol): TypeSymbol = internal.newTypeSymbol(NoSymbol, s.name.toTypeName, s.pos, if(s.isImplicit)Flag.IMPLICIT else NoFlags)
//    def clearTypeSymbol2(s: Symbol): TypeSymbol = internal.newTypeSymbol(NoSymbol, s.name.toTypeName, NoPosition, if(s.isImplicit)Flag.IMPLICIT else NoFlags)
//    def clearTypeDef(d: TypeDef): TypeDef = internal.typeDef(clearTypeSymbol(d.symbol))

    val companionSymbol: Symbol = caseClass.companion
    val info: Type = caseClass.info

    val primaryCtor: Symbol = caseClass.primaryConstructor
    val method = primaryCtor.asMethod
    val typeParams = info.typeParams.map(internal.typeDef(_))
//    val typeParams = info.typeParams.map(s => typeDef(newTypeSymbol(NoSymbol, s.name.toTypeName, NoPosition, NoFlags)))
//    val typeParams = info.typeParams.map(s => internal.typeDef(clearTypeSymbol2(s)))
    val typeArgs = info.typeParams.map(_.name)
    val paramLists = method.paramLists.map(_.map(internal.valDef(_)))
    val argLists = method.paramLists.map(_.map(_.asTerm.name))
    val baseType = info.baseType(atc.typeSymbol)
    val List(returnType) = baseType.typeArgs

    val methodName = TermName(smartName(caseClass.name.toString))

    val wellTyped =
      q"""
          def $methodName[..$typeParams](...$paramLists): $baseType =
            $companionSymbol[..$typeArgs](...$argLists)
        """


    wellTyped
  }
}

P.S。我一直在试验每个this article的toolbox.untypecheck / typecheck,但还没有找到合适的工作。

1 个答案:

答案 0 :(得分:1)

你需要使用

clas.typeArgs.map(_.toString).map(name => {
  TypeDef(Modifiers(Flag.PARAM),TypeName(name), List(),TypeBoundsTree(EmptyTree, EmptyTree))
      }

替换 info.typeParams.map(p => TypeName(p.name.toString))

这是我的代码

object GetSealedSubClass {

  def ol3[T]: Any = macro GetSealedSubClassImpl.ol3[T]
}

class GetSealedSubClassImpl(val c: Context) {

  import c.universe._

  def showInfo(s: String) =
    c.info(c.enclosingPosition, s.split("\n").mkString("\n |---macro info---\n |", "\n |", ""), true)

  def ol3[T: c.WeakTypeTag]: c.universe.Tree = {

    //get all sub class
    val subClass = c.weakTypeOf[T]
      .typeSymbol.asClass.knownDirectSubclasses
      .map(e => e.asClass.toType)

    //check type params must ia s sealed class
    if (subClass.size < 1)
      c.abort(c.enclosingPosition, s"${c.weakTypeOf[T]} is not a sealed class")

    // get sub class constructor params
    val subConstructorParams = subClass.map { e =>
      //get constructor
      e.members.filter(_.isConstructor)
        //if the class has many Constructor then you need filter the main Constructor
        .head.map(s => s.asMethod)
      //get function param list
    }.map(_.asMethod.paramLists.head)
      .map(_.map(e => q"""${e.name.toTermName}:${e.info} """))

    val outfunc = subClass zip subConstructorParams map {
      case (clas, parm) =>
        q"def smartConstructors[..${
          clas.typeArgs.map(_.toString).map(name => {
            TypeDef(Modifiers(Flag.PARAM), TypeName(name), List(), TypeBoundsTree(EmptyTree, EmptyTree))
          })
        }](..${parm})=${clas.typeSymbol.name.toTermName} (..${parm})"
    }

    val outClass =
      q"""
         object Term{
                 ..${outfunc}
                 }
          """
    showInfo(show(outClass))
    q"""{
      $outClass
        Term
      }
      """
  }
}

像这样使用

sealed trait Hello[A]

case class Ohayo[A, B](a: (A, B)) extends Hello[A]

object GetSealed extends App {
  val a = GetSealedSubClass.ol3[Hello[_]]
  val b=a.asInstanceOf[ {def smartConstructors[A, B](a: (A, B)): Ohayo[A, B]}].smartConstructors(1, 2).a
  println(b)
}