Когда выполняются аннотации макросов Scala? (макро рай)

Я попытался реализовать пример аннотаций макросов Scala, как описано в документации. . Мне удалось скомпилировать аннотации макросов до фактического проекта, который их использует, т. Е. @compileTimeOnly("enable macro paradise to expand macro annotations") не срабатывает, что означает, что аннотация макроса скомпилирована до ее использования. Все идет нормально.

Однако, когда я аннотирую определенные значения в моем реальном проекте следующим образом:

@identity val foo: Double = 1.1
@identity val bar: String = "bar"

то я ожидаю, что при запуске основного проекта произойдет следующая печать (по приведенному выше примеру аннотации макроса):

(<empty>,List(val foo: Double = 1.1))
(<empty>,List(val bar: String = "bar"))

Вот тут я запутался, печать не происходит при запуске основного проекта. Однако он появляется на долю секунды при компиляции основного проекта в качестве предупреждения?

(Я использую IntelliJ IDEA и Scala 2.12.8)


person Maarten    schedule 03.04.2019    source источник


Ответы (1)


Мне удалось скомпилировать аннотации макросов до фактического проекта, который их использует, т. Е. @compileTimeOnly («включить макрорай для расширения аннотаций макросов») не срабатывает, что означает, что аннотация макроса скомпилирована до ее использования

Нет, запуск @compileTimeOnly будет означать, что аннотация присутствует после компиляции кода, использующего ее. Таким образом, его отсутствие означает, что макрос был выполнен во время компиляции. И поскольку println находится в теле макроса, а не в преобразованном коде, именно тогда вы видите вывод.

Если вы хотите, чтобы печать выполнялась при запуске проекта, вам необходимо изменить возвращаемое значение, содержащее преобразованный код, то есть последние две строки в примере:

val outputs = expandees
c.Expr[Any](Block(outputs, Literal(Constant(()))))

либо используя квазицитаты, либо прямое управление AST.

Не проверено, но с использованием квазикавычек примерно так должно работать

object identityMacro {
  def impl(c: Context)(annottees: c.Expr[Any]*): c.Expr[Any] = {
    import c.universe._
    val inputs = annottees.map(_.tree).toList
    val (annottee, expandees) = inputs match {
      case (param: ValDef) :: (rest @ (_ :: _)) => (param, rest)
      case (param: TypeDef) :: (rest @ (_ :: _)) => (param, rest)
      case _ => (EmptyTree, inputs)
    }
    val stringToPrint = (annottee, expandees).toString
    c.Expr[Any](q"""
    println($stringToPrint)
    $expandees
    ()
    """)
  }
}
person Alexey Romanov    schedule 03.04.2019
comment
Спасибо, это решает мою проблему! Итак, если я правильно понимаю, это просто AST, который возвращается (в вашем случае построен с использованием квазикавычек) макросом, который расширяется в коде с использованием этого макроса? - person Maarten; 03.04.2019
comment
Да, это правильно. identityMacro.impl — это обычный метод Scala, который манипулирует AST и выполняется во время компиляции кода с использованием @identity. - person Alexey Romanov; 03.04.2019