首页 文章

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

提问于
浏览
4

我有一个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类时,它需要一个内部对象的集合,其大小根据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?

1 回答

  • 5

    您可以为 reify 提供类型参数以避免强制转换 . 将表达式的内部顺序转出来有点棘手,但并不多:

    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")))
    }
    

    生命是so much better in paradise,但是:

    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中可用作编译器插件 - 而不是reification和manual tree构造 .

相关问题