Scala 宏注解 - case class with type parameters
Scala macro annotation - case class with type parameters
我正在尝试为案例 classes 编写一个简单的宏注释,它向伴随对象添加一个方法。问题是新方法必须考虑带注释的案例 class.
上的类型参数
这是我需要通过的测试
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]
最终用例是生成可缓存类型的实例 class,它涉及超过 1k 行代码。非参数化版本已经可以工作了,这只是锦上添花。在 scalac 的幕后有一些我不了解但很想了解的东西。非常感谢您花时间阅读本文。
中使用 Scala 2.11.8
问题似乎是您在类型参数的位置使用了类型参数。这似乎可行(我还必须将类型参数添加到 defaultApply
方法声明中):
val typeArgs = typeParams.map(_.name)
val applyDef = q"""${name.toTermName}.apply[..$typeArgs]"""
c.warning(c.enclosingPosition, showRaw(applyDef))
q"""
object ${name.toTermName} {
def defaultApply[..$typeParams]:
(..${valueParams.map(_.tpt)}) => $name[..$typeArgs] = $applyDef
}
"""
我正在尝试为案例 classes 编写一个简单的宏注释,它向伴随对象添加一个方法。问题是新方法必须考虑带注释的案例 class.
上的类型参数这是我需要通过的测试
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]
最终用例是生成可缓存类型的实例 class,它涉及超过 1k 行代码。非参数化版本已经可以工作了,这只是锦上添花。在 scalac 的幕后有一些我不了解但很想了解的东西。非常感谢您花时间阅读本文。
中使用 Scala 2.11.8问题似乎是您在类型参数的位置使用了类型参数。这似乎可行(我还必须将类型参数添加到 defaultApply
方法声明中):
val typeArgs = typeParams.map(_.name)
val applyDef = q"""${name.toTermName}.apply[..$typeArgs]"""
c.warning(c.enclosingPosition, showRaw(applyDef))
q"""
object ${name.toTermName} {
def defaultApply[..$typeParams]:
(..${valueParams.map(_.tpt)}) => $name[..$typeArgs] = $applyDef
}
"""