如何通过scala宏注释定义更高kinded类型的类型别名(最好使用quasiquotes)

时间:2014-02-04 20:23:57

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

我开发了一个scala宏注释,它丰富了具有各种定义的对象(cf. play form macro)。除此之外,我还希望对象包含类型别名

type WFS = FS[_, _, _, _]

获取不同数量的通配符参数。

我已经尝试通过

提取单个通配符类型的值
q"type WFS = FS[_]" match { q"type WFS = FS[$t]" => t }

并希望在类型参数列表中使用提取的值(例如q“type WFS = FS [.. $ tplist]”)。但上述陈述产生错误:

scala> q"type WFS = FS[_]" match { case q"type WFS = FS[$t]" => t }
scala.MatchError: type WFS = FS[_$1] forSome { 
  <synthetic> type _$1 >: _root_.scala.Nothing <: _root_.scala.Any
} (of class scala.reflect.internal.Trees$TypeDef)
    at .<init>(<console>:15)
    at .<clinit>(<console>)
    at .<init>(<console>:7)
    at .<clinit>(<console>)
    at $print(<console>)
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43

还有另一种 - 也许更简单 - 构建必要树的方法吗?

1 个答案:

答案 0 :(得分:0)

错误是打印您匹配的值的实际形状,显然您需要在匹配的quasiquote中使用相同的形状:

scala> q"type WFS = FS[_]" match { case q"type WFS = FS[$a] forSome { $b }" => println(s"$a --- $b") }
_$1 --- <synthetic> type _$1 >: _root_.scala.Nothing <: _root_.scala.Any

如果您想要允许更多参数,而不仅仅是一个参数,最好使用"..$x"来允许:

scala> q"type WFS = FS[_]" match { case q"type WFS = FS[..$a] forSome { ..$b }" => println(s"$a --- $b") }
List(_$1) --- List(<synthetic> type _$1 >: _root_.scala.Nothing <: _root_.scala.Any)

通配符转换为存在 - FS[_]表示FS[T] forSome { type T }(有关存在性的详细信息,有“Scala语言规范”,第3.2.10节,“存在类型的占位符语法”)。 当你在匹配的quasiquote中写FS[$a]时,这意味着“我希望这里有一个类型构造函数应用于一个类型的参数”。但FS[T] forSome { type T }是一种存在类型,包含类型构造函数的应用程序,因此模式不匹配。

为了更好地理解这一点,并且知道如何调试类似的问题,使用showRaw查看由quasiquotes生成的树是很有见地的 - 因为这些是类型,我们需要类型quasiquotes,即{{1 }}:

tq"..."

据我了解,与quasiquotes匹配意味着匹配相应的树。所以上面说明了我描述的问题。

令人遗憾的是,quasiquotes并没有隐藏Scala类型系统的这些细节。 这可能是也可能不是quasiquotes的错误,但我不能对此发表评论 - 我猜如果不需要处理就会很酷