此文件頁面專屬於 Scala 2 中發布的功能,這些功能已在 Scala 3 中移除或由替代方案取代。除非另有說明,此頁面中的所有程式碼範例都假設您使用 Scala 2。
實驗性
Eugene Burmako
萃取巨集是 Scala 2.11.x 和 Scala 2.12.x 的一項功能,由 Paul Phillips 在 Scala 2.11.0-M5 中引入的基於名稱的萃取器啟用。萃取巨集不支援 Scala 2.10.x。它們也不支援 Scala 2.10.x 的巨集天堂。
模式
簡而言之,給定一個 unapply 方法(為簡化起見,在此範例中,scrutinee 為具體類型,但 extractor 也可以是多態的,如測試中所示)
def unapply(x: SomeType) = ???
可以使用呼叫目標 (c.prefix
) 和 scrutinee 的類型(與 x
一起提供)撰寫巨集,為 unapply 產生每次呼叫的萃取簽章,然後將這些簽章傳達給類型檢查器。
例如,以下是定義巨集的方式,該巨集會將 scrutinee 傳回樣式比對(有關如何表達涉及多個萃取項的簽章,請參閱 scala/scala#2848)。
def unapply(x: SomeType) = macro impl
def impl(c: Context)(x: c.Tree) = {
q"""
new {
class Match(x: SomeType) {
def isEmpty = false
def get = x
}
def unapply(x: SomeType) = new Match(x)
}.unapply($x)
"""
}
除了實作特定領域比對邏輯的比對器外,這裡還有相當多的樣板程式碼,但其中每個部分看起來對於安排與類型檢查器的非挫折對話都是必要的。也許可以在這個部門做得更好,但我看不到如何做到,而不用修改類型檢查器。
即使樣式使用結構類型,不知何故,並未產生任何反射呼叫(已透過 -Xlog-reflective-calls
驗證,然後透過手動檢查產生的程式碼驗證)。這對我來說是個謎,但這也是個好消息,因為這表示萃取器巨集不會造成效能損失。
幾乎。很遺憾的是,我無法將比對器轉換為值類別,因為無法宣告值類別為區域。儘管如此,我仍保留一個金絲雀 (neg/t5903e),一旦解除此限制,它將讓我們知道。
使用案例
特別是,此模式可用於實作字串內插器的變形模式比對器,而無需訴諸骯髒的技巧。例如,現在可以取消準引號非應用程式的硬編碼
def doTypedApply(tree: Tree, fun0: Tree, args: List[Tree], ...) = {
...
fun.tpe match {
case ExtractorType(unapply) if mode.inPatternMode =>
// this hardcode in Typers.scala is no longer necessary
if (unapply == QuasiquoteClass_api_unapply) macroExpandUnapply(...)
else doTypedUnapply(tree, fun0, fun, args, mode, pt)
}
}
此處的粗略實作策略包括撰寫一個萃取巨集,它會解構 c.prefix
,分析 StringContext
的部分,然後產生一個適當的比對器,如上所述。
請參閱我們的測試案例,網址為 run/t5903a、run/t5903b、run/t5903c、run/t5903d,以查看此實作以及萃取巨集的其他使用案例。