Macro to access source code of function at runtime

2019-05-05 09:46发布

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.

2条回答
兄弟一词,经得起流年.
2楼-- · 2019-05-05 10:37

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))
  }
}
查看更多
ゆ 、 Hurt°
3楼-- · 2019-05-05 10:48

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?

查看更多
登录 后发表回答