如何在scala宏中构建动态序列?

如何在scala宏中构建动态序列?,scala,scala-2.10,scala-macros,Scala,Scala 2.10,Scala Macros,我有一个scala宏,它输出嵌套的case类。我可以汇编使用reify创建的表达式片段,以编程方式构建嵌套的case类: 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

我有一个scala宏,它输出嵌套的case类。我可以汇编使用reify创建的表达式片段,以编程方式构建嵌套的case类:

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 )
除了恼人的演员阵容外,一切都很顺利(我如何才能解决这个问题?)。当我希望宏输出一些case类时,我遇到了麻烦,这些case类需要一组内部对象,这些对象的大小取决于marco在它处理的AST中看到的内容

因此,如果我有一个类,它接受一个foo序列:

 case class Baz(foos: Seq[Foo])
我想做以下几点:

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

您可以为
具体化提供类型参数,以避免强制转换。将表达式的顺序由内而外稍微复杂一点,但并不多:

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")))
}
然而,生活是:


这与第一个实现完全相同,但使用了quasiquotes,它现在可以在2.10中作为编译器插件使用,而不是具体化和手动构建树。

这是一个很好的答案。使用reify helper函数可以很好地组合。它们对应于使用带有小写字母的case类伴随对象,因此认知开销为零。它很快就编写了六个这样的助手,而不必让paradise编译器插件在大型代码库的ci构建中工作。另外,Quasiquetes语法需要学习,所以我认为对于这个简单的用例来说,使用模式匹配将输入AST映射到六个嵌套case类的丰富结构可能有些过分。再次感谢。
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")))
}