0

(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).

0__
  • 66,707
  • 21
  • 171
  • 266
  • 2
    I don't think you can get source, but you could get `Tree`: `def applyImpl(c: Context)(a: c.Expr[Expr], fun:c.Expr[Expr => Expr]): c.Expr[Map] = { val source = c.universe.show(fun.tree); ... }` – senia May 07 '13 at 17:00
  • @senia thanks for the link and the comment. I don't need the original source code, indeed, because it won't be shown (probably) to the user, I just want a source that I can recompile to the same tree, even if the serialised version of say `Tree` changes (so I don't want to serialise the tree directly). So, I'll have a look at this `universe.show`, that might indeed be sufficient. – 0__ May 07 '13 at 18:28

1 Answers1

0

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

Rex Kerr
  • 166,841
  • 26
  • 322
  • 407
  • I need to be able to do this at runtime with an embedded interpreter. In my compile project / IDE I already have access to the source code :) I could of course capture the text from the interpreter (and I've done that before), but then I need to fiddle around with finding the source locations (unless of course I add escape characters like you do with the `#`). – 0__ May 07 '13 at 18:25