为了学习Scala的宏系统,我想我应该尝试编写一个基本的CPS转换宏。我已经为Clojure编写了一个相当全面的CPS转换框架,所以我非常熟悉CPS转换本身。然而,我遇到了转换函数/方法应用程序的问题。
对于CPS转换,以下形式的函数调用:
cps(f(<a>, <b>, <c>, ...))需要翻译成以下形式的表达式:
cps(<a>){ $a =>
cps(<b>){ $b =>
cps(<c>){ $c =>
... => f($a, $b, $c, ...)
}
}
}显然,生成的延续lambda(例如,$a)的参数需要是新的符号,这样它们就不会无意中与词法上下文中的变量名冲突。因此,对于每个参数arg,我都会生成一个新名称:
val name = Ident(TermName(c.freshName))(其中c是宏的Context),然后我在下面的准引号中使用它:
q"""cps(arg)($name => $remainder)"""其中remainder指的是计算的其余部分。
宏本身编译得很好,但当我尝试将它与涉及函数应用程序的表达式一起使用时,我得到了以下错误:
... exception during macro expansion:
[error] java.lang.IllegalArgumentException: fresh$macro$1 is not valid representation of a parameter, consider reformatting it into q"val $name: $T = $default" shape但是,我不认为可以执行推荐的“重新格式化”,因为没有$default可以提供。
下面是一个最小的例子,它说明了我遇到的问题:
def id[A](expr : A) : A = macro idImpl[A]
def idImpl[A](c : blackbox.Context)(expr : c.Expr[A]) : c.Expr[A] = {
import c.universe._
val name = Ident(TermName(c.freshName))
//c.Expr(q"""val $name = $expr; $name""")
c.Expr(q"""($name => $name)($expr)""")
}请注意,如果您将lambda表达式替换为注释行,它将起作用。
所以我的问题是:如何将freshName生成的名称用作匿名函数的参数名称?
发布于 2017-09-26 08:31:32
我认为您需要一个val $name: $T = _形式的ValDef,其中_由NoTree表示。使用_初始化变量是有效的Scala,将对null的引用和对数字的引用设置为0 (即默认值),但当有ValDef(例如函数参数)但没有合理的默认值时,也用作内部占位符。
我在用手机,所以我不能测试。在任何情况下,如果NoTree不起作用,您都可以对REPL中的值q"a: Any => a"进行解构,以查找编译器通常设置为默认值的内容。
https://stackoverflow.com/questions/46414528
复制相似问题