在 scala 编译时反射中合并 `Tree` 和 `List[Tree]`
merge `Tree` and `List[Tree]` in scala compile-time reflection
我有 scala 宏,可以动态创建 class 的构造函数。
例如,如果我们有一个 class case class PersonConfig(name: String, age: Int, isFemale: Boolean)
。我有 class 名称的树结构和传递给 class 的参数,如下所示
@ val className = q"PersonConfig"
className: Ident = Ident(PersonConfig)
@ val args = List(q""""Jyn Erso"""", q"26", q"true")
args: List[Literal] = List(Literal(Constant("Jyn Erso")), Literal(Constant(26)), Literal(Constant(true)))
现在要创建将创建 PersonConfig
实例的 AST 结构(即 PersonConfig("Jyn Erso", 26, true)),我将不得不组合 className 和 args值。这里的挑战是 args
可以是任意大小,因为这个宏可用于为许多不同的 class 构造构造函数。
目前明显但不那么枯燥和冗长的解决方案是在 args
参数上进行模式匹配并创建 AST
结构,如下所示。
import scala.reflect.runtime.universe
def makeExpr(className: universe.Tree, args: List[universe.Tree]): universe.Tree = {
args.reverse match {
case node1 :: Nil => q"$className($node1)"
case arg1 :: arg2 :: Nil => q"$className($arg1, $arg2)"
case arg1 :: arg2 :: arg3 :: Nil => q"$className($arg1, $arg2, $arg3)"
case arg1 :: arg2 :: arg3 :: arg4 :: Nil => q"$className($arg1, $arg2, $arg3, $arg4)"
case arg1 :: arg2 :: arg3 :: arg4 :: arg5 :: Nil => q"$className($arg1, $arg2, $arg3, $arg4, $arg5)"
case Nil => throw new Exception(s"argument list for class ${className.toString} cannot be empty")
case _ => throw new Exception(s"argument list for class ${className.toString} is too long")
}
}
但是有没有更好的方法来有效地处理这个问题,哪个更 DRY?。例如使用 foldLeft 或其他等效方法来实现 makeExpr
功能的作用?
我设法使用 foldLeft 完成了这项工作,如下所示。
def makeExpr(c: blackbox.Context)(className: c.Tree, args: List[c.Tree]): c.universe.Tree = {
import c.universe._
args.reverse match {
case head :: tail => tail.foldLeft(q"$className($head)")({
case (q"$_(..$params)", node) => q"$className(..${params :+ node})" })
case Nil => throw new MacroException(s"argument list for class ${className.toString} cannot be empty")
}
}
我有 scala 宏,可以动态创建 class 的构造函数。
例如,如果我们有一个 class case class PersonConfig(name: String, age: Int, isFemale: Boolean)
。我有 class 名称的树结构和传递给 class 的参数,如下所示
@ val className = q"PersonConfig"
className: Ident = Ident(PersonConfig)
@ val args = List(q""""Jyn Erso"""", q"26", q"true")
args: List[Literal] = List(Literal(Constant("Jyn Erso")), Literal(Constant(26)), Literal(Constant(true)))
现在要创建将创建 PersonConfig
实例的 AST 结构(即 PersonConfig("Jyn Erso", 26, true)),我将不得不组合 className 和 args值。这里的挑战是 args
可以是任意大小,因为这个宏可用于为许多不同的 class 构造构造函数。
目前明显但不那么枯燥和冗长的解决方案是在 args
参数上进行模式匹配并创建 AST
结构,如下所示。
import scala.reflect.runtime.universe
def makeExpr(className: universe.Tree, args: List[universe.Tree]): universe.Tree = {
args.reverse match {
case node1 :: Nil => q"$className($node1)"
case arg1 :: arg2 :: Nil => q"$className($arg1, $arg2)"
case arg1 :: arg2 :: arg3 :: Nil => q"$className($arg1, $arg2, $arg3)"
case arg1 :: arg2 :: arg3 :: arg4 :: Nil => q"$className($arg1, $arg2, $arg3, $arg4)"
case arg1 :: arg2 :: arg3 :: arg4 :: arg5 :: Nil => q"$className($arg1, $arg2, $arg3, $arg4, $arg5)"
case Nil => throw new Exception(s"argument list for class ${className.toString} cannot be empty")
case _ => throw new Exception(s"argument list for class ${className.toString} is too long")
}
}
但是有没有更好的方法来有效地处理这个问题,哪个更 DRY?。例如使用 foldLeft 或其他等效方法来实现 makeExpr
功能的作用?
我设法使用 foldLeft 完成了这项工作,如下所示。
def makeExpr(c: blackbox.Context)(className: c.Tree, args: List[c.Tree]): c.universe.Tree = {
import c.universe._
args.reverse match {
case head :: tail => tail.foldLeft(q"$className($head)")({
case (q"$_(..$params)", node) => q"$className(..${params :+ node})" })
case Nil => throw new MacroException(s"argument list for class ${className.toString} cannot be empty")
}
}