Как построить динамическую последовательность в макросе scala?

У меня есть макрос 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 ответ

Решение

Вы можете предоставить параметр типа для 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")))
}

Жизнь в раю намного лучше, хотя:

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

Это в точности эквивалентно первой реализации, но использует квазицитаты - которые теперь доступны в 2.10 как плагин компилятора - вместо реификации и построения дерева вручную.

Другие вопросы по тегам