Использование макросов 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 кодирует захваченный текст. Вложение будет работать нормально, если вы работаете рекурсивно и знаете, что вложение может произойти.)