问题描述
我有一个输出嵌套 case 类的 Scala 宏.我可以组合使用 reify 创建的表达式片段以编程方式构建嵌套的 case 类:
I have a scala macro which outputs nested case classes. I can assemble fragments of expressions created using reify to build up the nested case classes programmatically:
case class Foo(name: String)
case class Bar(foo: Foo)
def foo(name: String) = {
c.universe reify {
Foo(c.literal(name).splice)
}
}
def bar(foo: Expr[Foo]) = {
c.universe reify {
Bar(foo.splice)
}
}
// output Bar(Foo("MyFoo"))
c.Expr( bar(foo("MyFoo").asInstanceOf[Expr[Foo]]).tree )
除了烦人的演员之外,一切都很好(我该如何解决?).我被卡住的地方是当我希望宏输出一些需要内部对象集合的案例类时,这些内部对象的大小根据 marco 在它正在处理的 AST 中看到的内容而变化.
Things work well apart from the annoying cast (how can i fix that?). Where I am stuck is when I want the macro to output some case class which require a collection of inner objects whos size varies based on what the marco sees within the AST it is processing.
因此,如果我有一个类采用 foo 序列:
So if I have a class which takes a sequence of foo:
case class Baz(foos: Seq[Foo])
我想做一些类似的事情:
I would like to do something along the lines of:
def baz(foos: Seq[Expr[Foo]]): Expr[Baz] = {
val flipped: Expr[Seq[Foo]] = ??? // convert foos from Seq[Expr[Foo]] to Expr[Seq[Foo]]
c.universe reify {
Baz(flipped.splice)
}
}
// now pack any number of Foos into an output Baz as required
c.Expr(baz(Seq(foo("MyFoo1"),foo("MyFoo2"))).tree)
我无法将 Seq[Expr[Foo]] 转换为 Expr[Seq[Foo]] 来进行拼接,这样我就可以将可变数量的嵌套对象打包到宏输出中.如何将动态构建的列表具体化以用作构造函数 arg?
I am unable to convert a Seq[Expr[Foo]] into an Expr[Seq[Foo]] to do the splice such that I can pack a variable number nested objects into the macro output. How can I reify a dynamically built list to use as the constructor arg?
推荐答案
您可以为 reify
提供类型参数以避免强制转换.将表达式序列由内而外转换有点棘手,但不是很多:
You can provide a type parameter to reify
to avoid the casts. Turning the sequence of expressions inside out is a little trickier, but not much:
case class Foo(name: String)
case class Bar(foo: Foo)
case class Baz(foos: Seq[Foo])
import scala.language.experimental.macros
import scala.reflect.macros.Context
def demo: Baz = macro demo_impl
def demo_impl(c: Context) = {
import c.universe._
def foo(name: String) = reify[Foo](Foo(c.literal(name).splice))
def bar(foo: Expr[Foo]) = reify[Bar](Bar(foo.splice))
def baz(foos: Seq[Expr[Foo]]): Expr[Baz] = {
val flipped: Expr[Seq[Foo]] = c.Expr[Seq[Foo]](
Apply(
Select(reify(Seq).tree, newTermName("apply")),
foos.map(_.tree).toList
)
)
reify(Baz(flipped.splice))
}
baz(Seq(foo("MyFoo1"), foo("MyFoo2")))
}
生活是天堂里好多了,不过:>
Life is so much better in paradise, though:
def demo: Baz = macro demo_impl
def demo_impl(c: Context) = {
import c.universe._
def foo(name: String) = c.Expr(q"Foo($name)")
def bar(foo: Expr[Foo]) = c.Expr(q"Bar($foo)")
def baz(foos: Seq[Expr[Foo]]) = c.Expr(q"Baz(Seq(..$foos))")
baz(Seq(foo("MyFoo1"), foo("MyFoo2")))
}
这与第一个实现完全等效,但使用了 quasiquotes(现在在 2.10 中作为编译器插件提供),而不是具体化和手动构建树.
This is exactly equivalent to the first implementation, but uses quasiquotes—which are now available in 2.10 as a compiler plugin—instead of reification and manual tree construction.
这篇关于如何在 Scala 宏中构建动态序列?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!