我在尝试使用Scala 2.11的新宏特性。我想看看是否能够进行以下重写:
forRange(0 to 10) { i => println(i) }
// into
val iter = (0 to 10).iterator
while (iter.hasNext) {
val i = iter.next
println(i)
}
我认为我通过这个宏已经相当接近了:
def _forRange[A](c: BlackboxContext)(range: c.Expr[Range])(func: c.Expr[Int => A]): c.Expr[Unit] = {
import c.universe._
val tree = func.tree match {
case q"($i: $t) => $body" => q"""
val iter = ${range}.iterator
while (iter.hasNext) {
val $i = iter.next
$body
}
"""
case _ => q""
}
c.Expr(tree)
}
当使用forRange(0 to 10) { i => println(i) }
调用时,将会产生以下输出结果 (至少,这是show
函数在结果树上给出的):
{
val iter = scala.this.Predef.intWrapper(0).to(10).iterator;
while$1(){
if (iter.hasNext)
{
{
val i = iter.next;
scala.this.Predef.println(i)
};
while$1()
}
else
()
}
}
那看起来应该能够工作,但是我的手动定义的val i 和被添加到函数体中的 i 引用之间存在冲突。我收到以下错误消息:
ReplGlobal.abort: symbol value i does not exist in$line38.$read$$iw$$iw$$iw$$iw$$iw$$iw$$iw$$iw. error: symbol value i does not exist in scala.reflect.internal.FatalError: symbol value i does not exist in $line38.$read$$iw$$iw$$iw$$iw$$iw$$iw$$iw$$iw.
然后是一堆很长的堆栈跟踪信息,导致出现了“Abandoned crashed session”通知。
我无法确定这是我的逻辑问题(你可能无法插入引用闭合变量的函数体),还是新实现中的错误。错误报告肯定可以更好些。由于我在Repl上运行这个程序,这种情况可能会更加严重。
是否有可能拆分函数,将函数体与闭合项分开,并重新编写以便将逻辑直接嵌入到结果树中?
resetAllAttrs
,你是否成功解决了这个问题?我遇到了完全相同的问题,我非常渴望解决它! - Andrew BateresetLocalAttrs
的函数,它应该可以解决大多数情况。不知道这是否适用于此情况,但值得一试。参考:http://docs.scala-lang.org/overviews/macros/changelog211.html - KChalouxresetLocalAttrs
已被重命名为untypecheck
。参考:https://github.com/scalamacros/resetallattrs - KChalouxuntypecheck
就足够了。然而,当我尝试匹配模式q"for ($i <- $collection) $body"
时,我需要旧的resetAllAttrs
(或者看起来是这样)。我已经在 Scala 2.11 宏中使用了 resetAllAttrs 库,并且它可以工作,而untypecheck
单独则不行,但是我很快就遇到了resetAllAttrs
破坏父树的常见问题。 - Andrew BateresetAllAttrs
,但他们之所以删除它,正是因为你提到的破坏树的问题。不过,如果你需要它,请查看 http://github.com/scalamacros/resetallattrs。 - KChaloux