此文件頁面專門針對 Scala 2 中發布的功能,這些功能已在 Scala 3 中移除或由替代方案取代。除非另有說明,此頁面中的所有程式碼範例都假設您使用的是 Scala 2。
Denys Shabalin 實驗性質
準引號是一種簡潔的表示法,讓您可以輕鬆操作 Scala 語法樹
scala> val tree = q"i am { a quasiquote }"
tree: universe.Tree = i.am(a.quasiquote)
每次你將程式碼片段包覆在 q"..."
中,它就會變成代表給定片段的樹狀結構。正如你可能已經注意到的,引號語法只是可擴充字串內插的另一種用法,在 2.10 中引入。儘管它們看起來像字串,但它們實際上是在語法樹上運作。
相同的語法可用於將樹狀結構與樣式相符
scala> println(tree match { case q"i am { a quasiquote }" => "it worked!" })
it worked!
每當你將樹狀結構與準引號相符時,只要給定樹狀結構的結構等於你提供為樣式的結構,它就會相符。你可以使用 equalsStructure
方法手動檢查結構相等性
scala> println(q"foo + bar" equalsStructure q"foo.+(bar)")
true
你也可以使用 $
將項目放入準引號中
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)
此操作也稱為取消引號。每當你在準引號中取消引號 Tree
類型的表達式時,它會將該樹狀結構結構性地替換到該位置。大部分時候,引號之間的此類替換等於原始碼的文字替換。
類似地,人們可以使用取消引號在樣式比對中結構性地解構樹狀結構
scala> val q"i am $what" = q"i am { a quasiquote }"
what: universe.Tree = a.quasiquote
內插器
Scala 是一種語法豐富的語言,會根據語法環境而有很大的不同
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))
}
在這個範例中,我們看到使用了三個主要的環境
List(1, 2)
和List(a + b)
是表達式List[Int]
是一種型態List(a, b)
是一種樣式
這些內容各自由一個獨立的內插器涵蓋
用於 | |
---|---|
q | 表達式、定義 和 匯入 |
tq | 類型 |
pq | 模式 |
不同內容之間的語法相似性並不表示底層樹之間的相似性
scala> println(q"List[Int]" equalsStructure tq"List[Int]")
false
如果我們窺探一下內部,我們會看到樹確實不同
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"))))
類似地,模式和表達式也不等價
scala> println(pq"List(a, b)" equalsStructure q"List(a, b)")
false
使用正確的內插器來建構有效的語法樹極為重要。
此外,還有兩個輔助內插器,讓您能夠處理 Scala 語法的小區域
用於 | |
---|---|
cq | 案例子句 |
fq | for 迴圈列舉器 |
請參閱章節 語法摘要 以取得詳細資料。
拼接
取消引號拼接是一種取消引號變數數量的變數
scala> val ab = List(q"a", q"b")
scala> val fab = q"f(..$ab)"
fab: universe.Tree = f(a, b)
取消引號註解之前的點表示扁平化的程度,稱為拼接等級。 ..$
預期參數為 Iterable[Tree]
,而 ...$
預期為 Iterable[Iterable[Tree]]
。
拼接可以輕鬆地與常規取消引號結合
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)
如果您想進一步抽象應用程式,可以使用 ...$
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)
目前 ...$
拼接僅支援 def
和 class
定義中的函式應用和參數清單。
類似於建構,也可以使用 ..$
和 ...$
來拆解樹狀結構
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))
在結合拼接與一般 $
變數萃取的方式上有一些限制
case q"f($first, ..$rest)" => // ok
case q"f(..$init, $last)" => // ok
case q"f(..$a, ..$b)" => // not allowed
因此,一般來說,每個給定的清單只允許一個 ..$
。類似的限制也適用於 ...$
case q"f(..$first)(...$rest)" => // ok
case q"f(...$init)(..$first)" => // ok
case q"f(...$a)(...$b)" => // not allowed
在本節中,我們只處理函式引數,但相同的拼接規則適用於所有具有可變元素數量的語法形式。語法摘要 和對應的詳細資料區段說明如何將拼接與其他語法形式搭配使用。