Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Macro to access source code of function at runtime

Using Scala macros I would like to get access to source code of function f.

Here is simplified example of my problem:

def logFImplementation(f: => Boolean) {
    val sourceCodeOfF: String = ... // <-- how to get source code of f??

    println("Scala source of f=" + sourceCodeOfF)
}

so for:

logFImplementation { val i = 5; List(1, 2, 3); true }

it should print:

Scala source of f=val i: Int = 5; immutable.this.List.apply[Int](1, 2, 3); true

(Right now I tested Macro to access source code text at runtime and it works great for { val i = 5; List(1, 2, 3); true }.logValueImpl but for f.logValueImpl it just prints f.)

Thanks for any advice.

like image 570
Artur Stanek Avatar asked Aug 20 '14 15:08

Artur Stanek


2 Answers

This is perhaps 'thread necromancy' as the question is quite old. I've had to grapple with this a bit.

import scala.reflect.macros.blackbox.Context
import scala.language.experimental.macros
object FunctionWrapper {

  implicit def apply[P, R](fn: P => R): FunctionWrapper[P, R] = macro apply_impl[P, R]

  def apply_impl[P: c.WeakTypeTag, R: c.WeakTypeTag](c: Context)(fn: c.Expr[P => R]): 
                  c.Expr[FunctionWrapper[P, R]] = {
    import c.universe._
     c.Expr(q" new FunctionWrapper($fn, ${show(fn.tree))})")
  }
}
class FunctionWrapper[P, R](val fn: P => R, description: String) 
      extends Function1[P, R] {
  def apply(p: P) = fn(p)
  override def toString = description
}

An example of it in use (has to be in a separate compilation module) is:

 object FunctionWrapperUser {
  def main(args: Array[String]): Unit = {
    val double = FunctionWrapper((i: Int) => i * 2)
    println(s"double to String is $double")
    println(double(4))
  }
}
like image 162
Stave Escura Avatar answered Nov 14 '22 12:11

Stave Escura


I seriously doubt that it is possible.

First, macros are compile-time thing. All they do is transform abstract syntax trees your program consists of. That's exactly the reason why you were able to do { val i = 5; List(1, 2, 3); true }.logValueImpl: AST is right here, so there is no difficulty printing it as string. But macros just can't resolve runtime values, because there are no macros at runtime.

Second, there is absolutely no reason to believe that f is Scala code. I could call your function from Java like this (not sure that I got all names right, but the idea is still the same):

someObject.logFImplementation(new scala.Function0<Boolean>() {
    @Override
    public Boolean apply() {
        // do some things in Java
    }
});

(and this is possible because internally call-by-name parameters are transformed into scala.Function0 objects). What would you expect your function to print in this case?

like image 22
Vladimir Matveev Avatar answered Nov 14 '22 11:11

Vladimir Matveev