【问题标题】:How to evaluate an expression inside a Scala macro?如何评估 Scala 宏中的表达式?
【发布时间】:2014-02-05 14:37:38
【问题描述】:

我正在尝试使用 Context#eval 方法评估宏内的 Expr

//Dummy implementation
def evalArrayTree(c: Context)(a: c.Expr[ArrayTree]): c.Expr[Array[Double]] = {
  import c.universe._
  println( c.eval(a) )

  val tree = reify( Array(0.0,0.0,0.0) ).tree 
  c.Expr[Array[Double]]( tree )
}

但是,编译器抱怨:

[error] /home/falcone/prg/sbt-example-paradise/core/src/main/scala/Test.scala:20: exception during macro expansion: 
[error] scala.tools.reflect.ToolBoxError: reflective toolbox has failed

如果在scala-user ML 中找到,则可以使用resetAllAttrs 解决问题。不过

  1. 我不明白我应该如何使用它。
  2. 这个函数seems to be deprecated

那么有办法解决我的问题吗?


其余代码:

object ArrayEval {

  import scala.language.experimental.macros

  def eval( a: ArrayOps.ArrayTree ): Array[Double] = macro Macros.evalArrayTree

}

object ArrayOps {

  sealed trait ArrayTree {
    def +( that: ArrayTree ) = Plus( this, that )
  }

  implicit class Ary( val ary: Array[Double] ) extends ArrayTree
  case class Plus( left: ArrayTree, right: ArrayTree ) extends ArrayTree

}

【问题讨论】:

  • 如果您可以在 github 项目中发布整个代码,将会很有帮助。然后,我会确保我们在修复 resetAttrs 时不会错过您的用例。

标签: scala macros scala-macros scala-macro-paradise


【解决方案1】:

c.eval 的文档确实告诉使用c.resetAllAttrs,但是这个函数有许多已知问题,有时会导致它无法修复地破坏它处理的树(这就是我们计划在 Scala 2.11 中删除它的原因- 我刚刚提交了一个拉取请求:https://github.com/scala/scala/pull/3485)。

您可以尝试使用c.resetLocalAttrs,它发生树损坏的可能性较小。不幸的是,它仍然有点坏。我们计划修复它 (https://groups.google.com/forum/#!topic/scala-internals/TtCTPlj_qcQ),但是在 Scala 2.10.x 和 2.11.0 中,将无法使 c.eval 可靠地工作。

【讨论】:

    【解决方案2】:

    嗯,我明白了他们使用resetAllAttrs 的含义。我的示例针对 Int 输入进行了简化,但我能够通过执行以下操作复制并修复您描述的错误:

    import scala.language.experimental.macros
    import scala.reflect.runtime.universe._
    import scala.reflect.macros.BlackboxContext
    
    def _evalMacro(c: BlackboxContext)(a: c.Expr[Int]) = {
      import c.universe._
    
      val treeReset = c.resetAllAttrs(a.tree) // Reset the symbols in the tree for 'a'
      val newExpr = c.Expr(treeReset)         // Construct a new expression for the updated tree
    
      println(c.eval(newExpr)) // Perform evaluation on the newly constructed expression
      ...                      // Do what you do
    }
    
    def evalMacro(a: Int) = macro _evalMacro
    

    我将猜测您可以使用resetAllAttrs,至少在某些未来版本的 Scala 出现之前是这样。 2.11 甚至没有给出弃用警告。

    注意:我使用的是 Scala 2.11。我相信这在 2.10 中应该是相同的,只是你将使用 Context 而不是 BlackboxContext

    【讨论】:

    • 我有更奇怪的错误:object Ary is not a member of package ArrayOps 这是真的,因为Ary 不是一个对象,而是一个隐式类,如上所示。
    • @paradigmatic 你在哪一行?您确定这是宏/上下文错误吗?我知道宏并不总是与隐式配合得很好,但我不确定到什么程度。
    • 我在这里添加了主要方法和错误:gist.github.com/paradigmatic/8827878Thanks
    • @paradigmatic 我可以让它在 repl 中工作,但是在编译过程中事情就崩溃了。不幸的是,这超出了我的范围,尤其是无法访问完整的代码库。你将不得不自己解决这个问题。这可能与 scala 对编译宏的限制有关(宏的编译需要在程序其余部分的编译之前进行)。
    • @Eugene 支持您的回答。所以现在它实际上会出现在我的上方:P
    猜你喜欢
    • 2018-09-09
    • 1970-01-01
    • 1970-01-01
    • 2020-01-26
    • 1970-01-01
    • 2019-11-01
    • 2021-03-28
    • 1970-01-01
    • 2013-11-20
    相关资源
    最近更新 更多