如何在Scala宏中的函数参数列表中使用新标识符

时间:2017-09-25 21:34:30

标签: scala macros metaprogramming

为了学习Scala的宏系统,我想我会尝试编写一个基本的CPS转换宏。我已经为Clojure编写了一个相当全面的CPS转换框架,所以我对CPS转换本身非常熟悉。但是,我无法转换功能/方法应用程序。

对于CPS转换,函数调用如下形式:

<parent>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-parent</artifactId>
    <version>1.5.7.RELEASE</version>
    <relativePath/>
</parent>

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-hateoas</artifactId>
</dependency>

需要翻译成以下形式的表达式:

cps(f(<a>, <b>, <c>, ...))

显然,生成的延续lambdas(例如cps(<a>){ $a => cps(<b>){ $b => cps(<c>){ $c => ... => f($a, $b, $c, ...) } } } )的参数需要是新符号,因此它们不会无意中与词汇上下文中的变量名冲突。因此,对于每个参数$a,我生成一个新名称:

arg

(其中val name = Ident(TermName(c.freshName)) 是宏的c),然后我在下面的quasiquote中使用它:

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

请注意,如果用注释行替换lambda表达式,它就可以工作。

所以我的问题:如何将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)""") } 生成的名称用作匿名函数的参数名称?

1 个答案:

答案 0 :(得分:-1)

我认为您需要ValDef表单val $name: $T = _,其中_NoTree表示。使用_初始化变量是有效的Scala,将对null的引用和数字设置为0(即默认值),但在ValDef时也用作内部占位符} s(例如函数参数)但没有合理的默认值。

我在这里移动,所以我无法测试它。在任何情况下,如果q"a: Any => a"不起作用,您可以在REPL中对值NoTree进行解构,以查找编译器通常设置的默认值。