Scala 准引用 - Quasiquote介绍

Quasiquotes are a neat notation that lets you manipulate Scala syntax trees with ease:

scala> val tree = q"i am { a quasiquote }"
tree: universe.Tree = i.am(a.quasiquote)

Every time you wrap a snippet of code in q"..." it will become a tree that represents a given snippet. As you might have already noticed, quotation syntax is just another usage of extensible string interpolation, introduced in 2.10. Although they look like strings they operate on syntactic trees under the hood.

The same syntax can be used to match trees as patterns:

scala> println(tree match { case q"i am { a quasiquote }" => "it worked!" })
it worked!

Whenever you match a tree with a quasiquote it will match whenever the structureof a given tree is equivalent to the one you've provided as a pattern. You can check for structural equality manually with the help of equalsStructure method:

scala> println(q"foo + bar" equalsStructure q"foo.+(bar)")
true

You can also put things into quasiquotation with the help of $:

scala> val aquasiquote = q"a quasiquote"
aquasiquote: universe.Select = a.quasiquote

scala> val tree = q"i am { $aquasiquote }" tree: universe.Tree = i.am(a.quasiquote) 

This operation is also known as unquoting. Whenever you unquote an expression of type Tree in a quasiquote it will structurally substitute that tree into that location. Most of the time such substitutions between quotes is equivalent to a textual substitution of the source code.

Similarly, one can structurally deconstruct a tree using unquoting in pattern matching:

scala> val q"i am $what" = q"i am { a quasiquote }"
what: universe.Tree = a.quasiquote 

Interpolators

Scala is a language with rich syntax that differs greatly depending on the syntactical context:

scala> val x = q"""
         val x: List[Int] = List(1, 2) match {
           case List(a, b) => List(a + b)
         }
       """
x: universe.ValDef =
val x: List[Int] = List(1, 2) match {
  case List((a @ _), (b @ _)) => List(a.$plus(b)) } 

In this example we see three primary contexts being used:

  1. List(1, 2) and List(a + b) are expressions
  2. List[Int] is a type
  3. List(a, b) is a pattern

Each of these contexts is covered by a separate interpolator:

 Used for
qexpressionsdefinitions and imports
tqtypes
pqpatterns

Syntactical similarity between different contexts doesn't imply similarity between underlying trees:

scala> println(q"List[Int]" equalsStructure tq"List[Int]")
false

If we peek under the hood we’ll see that trees are, indeed different:

scala> println(showRaw(q"List[Int]"))
TypeApply(Ident(TermName("List")), List(Ident(TypeName("Int")))) scala> println(showRaw(tq"List[Int]")) AppliedTypeTree(Ident(TypeName("List")), List(Ident(TypeName("Int")))) 

Similarly, patterns and expressions are also not equivalent:

scala> println(pq"List(a, b)" equalsStructure q"List(a, b)")
false

It’s extremely important to use the right interpolator for the job in order to construct a valid syntax tree.

Additionally there are two auxiliary interpolators that let you work with minor areas of scala syntax:

 Used for
cqcase clause
fqfor loop enumerator

See the section syntax summary for details.

Splicing

Unquote splicing is a way to unquote a variable number of elements:

scala> val ab = List(q"a", q"b")
scala> val fab = q"f(..$ab)" fab: universe.Tree = f(a, b) 

Dots before the unquotee annotate indicate a degree of flattenning and are called a splicing rank..$ expects the argument to be an Iterable[Tree] and ...$ expects an Iterable[Iterable[Tree]].

Splicing can easily be combined with regular unquotation:

scala> val c = q"c"
scala> val fabc = q"f(..$ab, $c)" fabc: universe.Tree = f(a, b, c) scala> val fcab = q"f($c, ..$ab)" fcab: universe.Tree = f(c, a, b) scala> val fabcab = q"f(..$ab, $c, ..$ab)" fabcab: universe.Tree = f(a, b, c, a, b) 

If you want to abstract over applications even further, you can use ...$:

scala> val argss = List(ab, List(c))
arglists: List[List[universe.Ident]] = List(List(a, b), List(c)) scala> val fargss = q"f(...$argss)" fargss: universe.Tree = f(a, b)(c) 

At the moment ...$ splicing is only supported for function applications and parameter lists in def and class definitions.

Similarly to construction one can also use ..$ and ...$ to tear trees apart:

scala> val q"f(..$args)" = q"f(a, b)"
args: List[universe.Tree] = List(a, b) scala> val q"f(...$argss)" = q"f(a, b)(c)" argss: List[List[universe.Tree]] = List(List(a, b), List(c)) 

There are some limitations in the way you can combine splicing with regular $variable extraction:

case q"f($first, ..$rest)" => // ok case q"f(..$init, $last)" => // ok case q"f(..$a, ..$b)" => // not allowed 

So, in general, only one ..$ is allowed per given list. Similar restrictions also apply to ...$:

case q"f(..$first)(...$rest)" => // ok case q"f(...$init)(..$first)" => // ok case q"f(...$a)(...$b)" => // not allowed 

In this section we only worked with function arguments but the same splicing rules are true for all syntax forms with a variable number of elements. Syntax summaryand the corresponding details sections demonstrate how you can use splicing with other syntactic forms.

转载于:https://www.cnblogs.com/barrywxx/p/10804587.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值