Pregunta

(for TL;DR, go to the bold face part)

I am having a clean closed type class system with serialization (detached from POJO serialization woes). For example:

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

But in situations I need to capture a closure. For example:

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

Now, I had solved this once with POJO serialization (ObjectOutputStream etc.) for the fun. I got badly bitten in the feet, because I couldn't read in Scala 2.10 what I had serialised in 2.9. And in this case, I really need to make sure I can get my stuff back independent of the Scala version.

So... I have been thinking that I could use a macro to make a "backup" of the source code, so that if POJO deserialisation fails, I can regenerate the function from source (using an in-place compiler/interpreter).

My idea would be

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

Is it possibly to easily capture the source of a call like

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

(My guess is the def-macro needs to be already in the map function of ExprOps).

¿Fue útil?

Solución

Text replacement macros are awesome at this kind of thing. Scala doesn't come with them, but consider writing your own! Transforming e.g.

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

to

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

should be pretty easy; then you just have to preprocess before you compile. If you want your IDE to not get confused by different line lengths, you can store the strings in a separate object, e.g.

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

goes to

({_+7}, Store.aW4)

...

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

(For best results, base-64 encode the captured text. Nesting will work fine as long as you work recursively and are aware that nesting might happen.)

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top