5

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.

Community
  • 1
  • 1

2 Answers2

5

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))
  }
}
Stave Escura
  • 2,058
  • 1
  • 19
  • 24
2

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?

Vladimir Matveev
  • 120,085
  • 34
  • 287
  • 296
  • Thank you very much for your answer. Yes, everything is correct, it is Function0 and I agree with your answer completely. The question to you is: do you see any possible ways/techniques that will allow to grab Scala code from which Function0 was created by compiler? (and have access to this string at runtime) – Artur Stanek Aug 20 '14 at 17:31
  • No, there is no way. Scala does not save AST to the class files (and there is likely no space for it anyway), and you just can't reliably recover Scala code from byte code, and even if it did save AST, you just *couldn't* extract it from `f` using macros because macros work at compile time, and `f`s actual class (and hence implementation) is unknown util runtime. – Vladimir Matveev Aug 20 '14 at 17:38
  • 1
    @VladimirMatveev, I support your point of view, but I'd like to mention that in `scala.meta` (next generation of scala) there will be such a possibility (i.e. to carry AST to runtime), so there certainly *is* a place. Anyway, everything what you said is correct as for now. – tkroman Aug 20 '14 at 21:06
  • Again thank you both for your answers. Maybe I can reformulate my question: how to create macro `logImplementation{ val i = 5; List(1, 2, 3); true }` that will print passed piece of code and return boolean value of evaluation? (If you want I can create next question at StackOverflow so community will not loose your advices.) – Artur Stanek Aug 20 '14 at 23:10
  • I posted other question: http://stackoverflow.com/questions/25418685/how-to-print-source-code-of-if-condition-in-then – Artur Stanek Aug 21 '14 at 05:15