【问题标题】:Macro to access source code of function at runtime在运行时访问函数源代码的宏
【发布时间】:2014-08-20 15:53:04
【问题描述】:

使用 Scala 宏我想访问函数 f 的源代码。

这是我的问题的简化示例:

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

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

所以:

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

应该打印出来:

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

(现在我测试了Macro to access source code text at runtime,它适用于{ val i = 5; List(1, 2, 3); true }.logValueImpl,但对于f.logValueImpl,它只打印f。)

感谢您的建议。

【问题讨论】:

标签: scala reflection macros scala-macros


【解决方案1】:

这可能是“线程死灵术”,因为这个问题已经很老了。我不得不努力解决这个问题。

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
}

它的一个使用示例(必须在单独的编译模块中)是:

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

【讨论】:

    【解决方案2】:

    严重怀疑这是可能的。

    首先,宏是编译时的东西。他们所做的只是转换您的程序所包含的抽象语法树。这正是您能够做到{ val i = 5; List(1, 2, 3); true }.logValueImpl 的原因:AST 就在这里,因此将其打印为字符串没有任何困难。但是宏无法解析运行时值,因为运行时没有宏。

    其次,绝对没有理由相信f 是 Scala 代码。我可以像这样从 Java 调用你的函数(不确定我的名字是否正确,但想法仍然相同):

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

    (这是可能的,因为内部按名称调用的参数被转换为scala.Function0 对象)。在这种情况下,您希望您的函数打印什么?

    【讨论】:

    • 非常感谢您的回答。是的,一切都是正确的,它是 Function0,我完全同意你的回答。对您的问题是:您是否看到任何可能的方法/技术可以获取编译器从中创建 Function0 的 Scala 代码? (并且可以在运行时访问这个字符串)
    • 不,没有办法。 Scala 不会将 AST 保存到类文件中(而且很可能没有空间存放它),而且您无法从字节码中可靠地恢复 Scala 代码,即使它确实保存了 AST,您也只是 couldn' t 使用宏从 f 中提取它,因为宏在编译时工作,而 fs 的实际类(以及因此实现)是未知的 util 运行时。
    • @VladimirMatveev,我支持你的观点,但我想提一下,在scala.meta(下一代 scala)中会有这样的可能性(即携带 AST 到运行时) ,所以肯定有一个地方。无论如何,你说的一切就目前而言都是正确的。
    • 再次感谢两位的回答。也许我可以重新提出我的问题:如何创建宏 logImplementation{ val i = 5; List(1, 2, 3); true } 来打印通过的代码并返回评估的布尔值? (如果你愿意,我可以在 StackOverflow 上创建下一个问题,这样社区就不会失去你的建议。)
    • 我发布了其他问题:stackoverflow.com/questions/25418685/…
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2013-01-31
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2010-10-02
    • 1970-01-01
    相关资源
    最近更新 更多