Scala宏注释 - 具有类型参数的案例类

时间:2016-07-05 21:10:28

标签: scala macros scala-2.11 scala-quasiquotes scala-macro-paradise

我正在尝试为case类编写一个简单的宏注释,它为随播对象添加了一个方法。问题是新方法必须考虑带注释的案例类的类型参数。

这是我需要传递的测试

package my.macros

import org.scalatest._

class DefaultApplyTest extends FlatSpec with Matchers {

  @defaultApply case class Generic[A, B](a: A, b: B)

  it should "define defaultApply method in companion object" in {
    assert(Generic.defaultApply("str", 1) == Generic("str", 1))
  }
}

以下是我为完成此操作而编写的代码

package my.macros

import scala.reflect.macros._
import scala.language.experimental.macros
import scala.annotation.StaticAnnotation

class defaultApply extends StaticAnnotation {
  def macroTransform(annottees: Any*): Any = macro DefaultApply.impl
}

object DefaultApply {

  def impl(c: blackbox.Context)(annottees: c.Expr[Any]*): c.Expr[Any] = {
    import c.universe._

    def defaultApplyCompanion(classDecl: ClassDef) = {
      val (name, typeParams, valueParams) = try {
        val q"case class ${name: TypeName}[..${typeParams: Seq[TypeDef]}](..${valueParams: Seq[ValDef]}) extends ..$bases { ..$body }" = classDecl
        (name, typeParams, valueParams)
      } catch {
        case e: MatchError =>
          c.warning(c.enclosingPosition, e.toString)
          c.abort(c.enclosingPosition, "Annotation is only supported on case class")
      }

      val applyDef = q"""${name.toTermName}.apply[..$typeParams]"""
      c.warning(c.enclosingPosition, showRaw(applyDef))

      q"""
        object ${name.toTermName} {
          def defaultApply: (..${valueParams.map(_.tpt)}) => $name[..$typeParams] = $applyDef
        }
      """
    }

    def modifiedDeclaration(classDecl: ClassDef) = {
      val compDecl = defaultApplyCompanion(classDecl)

      c.Expr(q"""
        $classDecl
        $compDecl
      """)
    }

    annottees.map(_.tree) match {
      case (classDecl: ClassDef) :: Nil => modifiedDeclaration(classDecl)
      case _ => c.abort(c.enclosingPosition, "Invalid annottee")
    }
  }
}

我理解的问题是,当我尝试lift结果语法树中的类型参数列表时,它们不会被识别为与原始树相同的类型参数。

所以我关注的是宏的这部分

  val applyDef = q"""${name.toTermName}.apply[..$typeParams]"""
  c.warning(c.enclosingPosition, showRaw(applyDef))

原始语法树以

的形式发出
TypeApply(Select(Ident(TermName("Generic")), TermName("apply")), List(TypeDef(Modifiers(PARAM), TypeName("A"), List(), TypeBoundsTree(EmptyTree, EmptyTree)), TypeDef(Modifiers(PARAM), TypeName("B"), List(), TypeBoundsTree(EmptyTree, EmptyTree))))

但编译器对此

不满意
type arguments [<notype>,<notype>] do not conform to method apply's type parameter bounds [A,B]

最终用例是生成可缓存类型类的实例,该类触及超过1k行代码。非参数化版本已经有效,这只是锦上添花。我不明白scalac的东西,但我想。花在阅读本文上的时间非常感谢。

我正在使用Scala 2.11.8和macro paradise 2.1.0

1 个答案:

答案 0 :(得分:0)

问题似乎是您在类型参数的位置使用类型参数。这似乎有效(我还必须将类型参数添加到In [153]: df = df.groupby('A', as_index=False)['B1'].agg(['sum','count']).reset_index() In [154]: df = pd.merge(df3, df, on='A', how='left').rename(columns={'B1':'Sum'}).fillna('') In [155]: df Out[155]: A B sum count 0 Erick Yes 1 John Yes 18 3 2 David No 3 Mandy Yes 29 2 4 Jared No 5 Lisa No 方法声明中):

defaultApply