Quasiquotes令人惊讶 - 他们在Scala中编写宏非常不那么痛苦,而且根据我的经验,他们几乎总是按照我的预期完成工作。最重要的是,它们现在在Scala 2.10中可用as a plugin。
这个问题是关于我在编写this blog post时遇到的一个小问题。当我找到几分钟时,我会在我的列表中查看,但我想我会在这里发布,以防其他人可以打败我,并帮助其他人遇到同样的问题。< / p>
假设我有一个名称类型对列表的列表:
val pss = List(
List(newTermName("x") -> typeOf[Int], newTermName("y") -> typeOf[Char]),
List(newTermName("z") -> typeOf[String])
)
我想把它们变成一棵看起来像这样的树:
def foo(x: Int, y: Char)(z: String) = ???
以下工作正常:
q"def bar(${pss.head.head._1}: ${pss.head.head._2}) = ???"
也就是说,它构建了以下树:
def bar(x: Int) = ???
这表明我应该能够写出这样的东西:
val quoted = pss.map(_.map { case (n, t) => q"$n: $t" })
q"def foo..${quoted.map(ps => q"($ps)")} = 1"
或者更简单,在单个参数列表中有多个参数:
q"def baz(..${quoted.head}) = ???"
两者都没有效果 - 我得到这样的错误:
<console>:28: error: type mismatch;
found : List[c.universe.Typed]
required: List[c.universe.ValDef]
q"def baz(..${quoted.head}) = ???"
^
足够公平 - 我可以看到它对quasiquoter的看法,就像我正在构建类型化表达式而不是在quoted
中定义参数。没有一个我能想到的显而易见的事情(添加= _
,明确地将quasiquote键入ValDef
等)。
我知道我可以手动构建参数定义:
val valDefs = pss.map(
_.map {
case (n, t) => ValDef(Modifiers(Flag.PARAM), n, TypeTree(t), EmptyTree)
}
)
现在baz
版本(带有一个参数列表)可以工作:
q"def baz(..${valDefs.head}) = ???"
但不是foo
版本(具有多个参数列表的版本)。
所以这里有两个问题。首先,如何使用quasiquotes将名称类型对转换为引用参数列表上下文之外的参数ValDef
?第二,如何将参数定义列表列表转换为多个参数列表?
很容易回到整个该死的东西的手动AST构造(参见my post的例子),但我希望能够使用quasiquotes代替。
答案 0 :(得分:4)
以下是您问题的快速解决方案:
val pss = List(
List(newTermName("x") -> typeOf[Int], newTermName("y") -> typeOf[Char]),
List(newTermName("z") -> typeOf[String])
)
val vparamss: List[List[ValDef]] = pss.map { _.map { case (name, tpe) => q"val $name: $tpe" } }
q"def foo(...$vparamss)"
正如你所看到的那样...... $ splice允许你定义一个带有多个参数列表的函数。函数参数本身的表示方式与常规值相同。
另一个例子,其中...... $可能有用:
val xy = List(List(q"x"), List(q"y"))
q"f(...$xy)" // same as q"f(x)(y)"