Использование макросов def для захвата исходного кода

(для TL;DR перейдите к части, выделенной жирным шрифтом)

У меня чистая система классов закрытого типа с сериализацией (отсоединена от проблем сериализации POJO). Например:

trait Expr
case class Const(i: Int) extends Expr
case class BinOp(a: Expr, b: Expr, op: Int) extends Expr

Но в ситуациях мне нужно запечатлеть замыкание. Например:

case class Map(a: Expr, fun: Expr => Expr) extends Expr

Теперь я решил это один раз с сериализацией POJO (ObjectOutputStream и т. д.) для fun, Я сильно укусил в ноги, потому что я не мог прочитать в Scala 2.10 то, что я сериализовал в 2.9. И в этом случае мне действительно нужно убедиться, что я могу вернуть свои вещи независимо от версии Scala.

Итак... Я думал, что мог бы использовать макрос для создания "резервной копии" исходного кода, чтобы в случае сбоя десериализации POJO я мог восстановить функцию из источника (используя компилятор / интерпретатор на месте).

Моя идея была бы

object Map {
  def apply(a: Expr, fun: Expr => Expr): Map = macro applyImpl
  private def applyImpl = ???

  def unapply(m: Map): Option[(Expr, Expr => Expr)] = Some(m.a -> m.fun)
}
trait Map extends Expr {
  def a: Expr
  def fun: Expr => Expr
}

implicit class ExprOps(val ex: Expr) extends AnyVal {
  def map(fun: Expr => Expr) = Map(ex, fun)
}

Возможно ли легко захватить источник вызова, как

//           |------------- source of this -------------|
someExpr.map { case Const(i) => Const(i*i); case x => x }

(Мое предположение, что def-макрос должен быть уже в map функция ExprOps).

1 ответ

Решение

Макросы замены текста просто великолепны. Scala не идет с ними, но подумайте над тем, чтобы написать свой собственный! Преобразование например

{# case Const(i) => Const(i*i); case x => x #}

в

({ case Const(i) => Const(i*i); case x => x }, """case Const(i) => Const(i*i); case x => x""")

должно быть довольно легко; тогда вам просто нужно выполнить предварительную обработку перед компиляцией. Если вы хотите, чтобы ваша IDE не была запутана разными длинами строк, вы можете хранить строки в отдельном объекте, например

{# _+7 #}/*STORE*/

идет к

({_+7}, Store.aW4)

...

object Store {
   val aW4 = """_+7"""
}
//(EOF)

(Для достижения наилучших результатов base-64 кодирует захваченный текст. Вложение будет работать нормально, если вы работаете рекурсивно и знаете, что вложение может произойти.)

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