我正在玩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:符号值i在$ line38中不存在.$ read $$ iw $$ iw $$ iw $$ iw $$ iw $$ iw $$ iw $$ iw.错误:符号值我不存在于scala.reflect.internal.FatalError:符号值i在$ line38中不存在.$ read $$ iw $$ iw $$ iw $$ iw $$ iw $$ iw $$ iw $$ IW.
然后是一个相当大的堆栈跟踪,导致"Abandoned崩溃会话"通知.
我无法判断这是否是我的逻辑问题(你根本无法在引用封闭变量的函数体中拼接),或者它是否是新实现的错误.错误报告肯定会更好.可能因为我在Repl上运行它而加剧了这一点.
是否可以拆分一个函数,将正文与封闭的术语分开,并重写它以便将逻辑直接拼接到结果树中?
如有疑问,请resetAllAttrs
:
import scala.language.experimental.macros import scala.reflect.macros.BlackboxContext 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 ${c.resetAllAttrs(body)} // The only line I've changed. } """ case _ => q"" } c.Expr(tree) }
然后:
scala> def forRange[A](range: Range)(func: Int => A) = macro _forRange[A] defined term macro forRange: [A](range: Range)(func: Int => A)Unit scala> forRange(0 to 10) { i => println(i) } 0 1 2 3 4 5 6 7 8 9 10
一般来说,当你从一个地方抓住一棵树并把它扔到其他地方时,可能需要resetAllAttrs
用来使所有的符号都正确.
Oscar Boykin 在推特上指出我之前的回答已经不再有效了,反正它也不是一个非常完整的答案 - 它解决了SC在Scala 2.10上指出的问题,但它并不小心卫生 - 如果你写了iter => println(iter)
你的话例如,d会出现编译时失败.
2.11的更好实现将使用a Transformer
在取消对它进行非类型化后重写树:
import scala.language.experimental.macros import scala.reflect.macros.blackbox.Context def _forRange[A](c: Context)(r: c.Expr[Range])(f: c.Expr[Int => A]): c.Tree = { import c.universe._ f.tree match { case q"($i: $_) => $body" => val newName = TermName(c.freshName()) val transformer = new Transformer { override def transform(tree: Tree): Tree = tree match { case Ident(`i`) => Ident(newName) case other => super.transform(other) } } q""" val iter = ${r.tree}.iterator while (iter.hasNext) { val $newName = iter.next ${ transformer.transform(c.untypecheck(body)) } } """ } } def forRange[A](r: Range)(f: Int => A): Unit = macro _forRange[A]
其工作方式如下:
scala> forRange(0 to 10)((i: Int) => println(i)) 0 1 2 3 4 5 6 7 8 9 10
现在我们在函数文字中使用的变量名称无关紧要,因为它只会被一个新变量替换.