多个参数和参数列表的准报价 [英] Quasiquotes for multiple parameters and parameter lists

查看:102
本文介绍了多个参数和参数列表的准报价的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

Quasiquotes 令人惊叹-它们使在Scala中编写宏的痛苦大大减轻了,而在我经验,他们几乎总是完全按照我的期望工作.最好的是,它们现在可以在Scala 2.10中作为插件使用作为插件.

这个问题是关于我在撰写此博客文章时遇到的一个小问题.当我可以找到几分钟的时候,这是我要查找的东西的清单,但是我认为我会在这里张贴它,以防别人打败我,并帮助遇到相同问题的其他人. /p>

假设我有一个名称-类型对的列表列表:

val pss = List(
  List(newTermName("x") -> typeOf[Int], newTermName("y") -> typeOf[Char]),
  List(newTermName("z") -> typeOf[String])
)

我想将它们变成一棵看起来像这样的树:

def foo(x: Int, y: Char)(z: String) = ???

以下工作正常:

q"def bar(${pss.head.head._1}: ${pss.head.head._2}) = ???"

也就是说,它会构建以下树:

def bar(x: Int) = ???

哪个建议我应该能够写这样的东西:

val quoted = pss.map(_.map { case (n, t) => q"$n: $t" })

q"def foo..${quoted.map(ps => q"($ps)")} = 1"

或者更简单一点,在一个参数列表中包含多个参数:

q"def baz(..${quoted.head}) = ???"

都不起作用-我收到这样的错误:

<console>:28: error: type mismatch;
 found   : List[c.universe.Typed]
 required: List[c.universe.ValDef]
           q"def baz(..${quoted.head}) = ???"
                                ^

足够好-我可以看到拟报价员的样子,就像我在构建类型化表达式而不是在quoted中定义参数一样.我想尝试尝试的所有显而易见的事情都没有奏效(添加= _,将准引用显式键入为ValDef等).

我知道我可以手动构建参数定义:

val valDefs = pss.map(
  _.map {
    case (n, t) => ValDef(Modifiers(Flag.PARAM), n, TypeTree(t), EmptyTree)
  }
)

现在baz版本(带有一个参数列表)可以工作:

q"def baz(..${valDefs.head}) = ???"

但不是foo版本(具有多个参数列表的版本).

所以这里有两个问题.首先,如何在引用的参数列表的上下文之外使用准引用将名称类型对转换为参数ValDef?其次,如何将参数定义列表列表转换为多个参数列表?

对于整个该死的事情,很容易退回到手动AST构造(请参阅我的帖子),但是我希望能够使用准引用.

解决方案

以下是您解决问题的快速方法:

val pss = List(
  List(newTermName("x") -> typeOf[Int], newTermName("y") -> typeOf[Char]),
  List(newTermName("z") -> typeOf[String])
)
val vparamss: List[List[ValDef]] = pss.map { _.map { case (name, tpe) => q"val $name: $tpe" } }
q"def foo(...$vparamss)"

如您所见,特殊的... $ splice允许您定义具有多个参数列表的函数.函数参数本身的表示方式与常规val相同.

另一个可能在其中使用... $的示例:

val xy = List(List(q"x"), List(q"y"))
q"f(...$xy)" // same as q"f(x)(y)"

Quasiquotes are amazing—they make writing macros in Scala hugely less painful, and in my experience they almost always just work exactly as I'd expect. And best of all, they're now available as a plugin in Scala 2.10.

This question is about a small problem that I ran into while writing this blog post. It's on my list of stuff to look into when I can find a few minutes, but I figured I'd post it here in case someone else can beat me to it, and to help other people who are running into the same question.

Suppose I have a list of lists of name-type pairs:

val pss = List(
  List(newTermName("x") -> typeOf[Int], newTermName("y") -> typeOf[Char]),
  List(newTermName("z") -> typeOf[String])
)

I want to turn these into a tree that looks like this:

def foo(x: Int, y: Char)(z: String) = ???

The following works just fine:

q"def bar(${pss.head.head._1}: ${pss.head.head._2}) = ???"

That is, it builds the following tree:

def bar(x: Int) = ???

Which suggests that I should be able to write something like this:

val quoted = pss.map(_.map { case (n, t) => q"$n: $t" })

q"def foo..${quoted.map(ps => q"($ps)")} = 1"

Or a little more simply, with multiple parameters in a single parameter list:

q"def baz(..${quoted.head}) = ???"

Neither works—I get errors like this:

<console>:28: error: type mismatch;
 found   : List[c.universe.Typed]
 required: List[c.universe.ValDef]
           q"def baz(..${quoted.head}) = ???"
                                ^

Fair enough—I can see how it would look to the quasiquoter like I'm building typed expressions rather than defining parameters in quoted. None of the obvious things I could think to try worked (adding = _, explicitly typing the quasiquote as a ValDef, etc.).

I know that I can build the parameter definitions manually:

val valDefs = pss.map(
  _.map {
    case (n, t) => ValDef(Modifiers(Flag.PARAM), n, TypeTree(t), EmptyTree)
  }
)

And now the baz version (with one parameter list) works:

q"def baz(..${valDefs.head}) = ???"

But not the foo version (the one with multiple parameter lists).

So there are two questions here. First, how can I use quasiquotes to turn a name-type pair into a parameter ValDef outside of the context of a quoted parameter list? And second, how can I turn a list of lists of parameter definitions into multiple parameter lists?

It's easy enough to fall back to manual AST construction for the whole damn thing (see my post for an example), but I'd like to be able to use quasiquotes instead.

解决方案

Here is the quick solution to your problem:

val pss = List(
  List(newTermName("x") -> typeOf[Int], newTermName("y") -> typeOf[Char]),
  List(newTermName("z") -> typeOf[String])
)
val vparamss: List[List[ValDef]] = pss.map { _.map { case (name, tpe) => q"val $name: $tpe" } }
q"def foo(...$vparamss)"

As you can see special ...$splice lets you define a function with multiple argument lists. Function arguments themselves are represented in the same manner as regular vals.

Another example where ...$it might be useful:

val xy = List(List(q"x"), List(q"y"))
q"f(...$xy)" // same as q"f(x)(y)"

这篇关于多个参数和参数列表的准报价的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆