此文件頁面特定於 Scala 2 中運送的功能,這些功能已在 Scala 3 中移除或由其他功能取代。除非另有說明,此頁面中的所有程式碼範例都假設您使用的是 Scala 2。
實驗性
Eugene Burmako
將巨集區分為黑盒巨集和白盒巨集是 Scala 2.11.x 和 Scala 2.12.x 的功能。Scala 2.10.x 不支援黑盒/白盒區分。Scala 2.10.x 的巨集天堂也不支援。
巨集如何運作?
隨著巨集成為 Scala 2.10 正式版本的一部分,研究和產業的程式設計師找到了使用巨集來解決各種問題的創新方法,遠遠超出了我們最初的預期。
事實上,巨集很快地成為我們生態系統中重要的一部分,就在 Scala 2.10 發布後的幾個月,當巨集以實驗性質引入時,我們召開了 Scala 語言團隊會議,並決定標準化巨集,並在 2.12 中使其成為 Scala 的一個成熟功能。
更新事實證明,在 Scala 2.12 中穩定巨集並非易事。我們對此進行的研究導致為 Scala 建立了一個新的元程式設計基礎,稱為 scala.meta,預計其第一個測試版將與 Scala 2.12 同時發布,並可能在後續版本的 Scala 中包含。在此期間,Scala 2.12 對於反射和巨集不會有任何變更 - 所有內容都將保持與 Scala 2.10 和 Scala 2.11 中的實驗性質相同,且不會移除任何功能。然而,儘管撰寫本文的環境已發生變化,但資訊仍然相關,因此請繼續閱讀。
巨集的類型繁多,因此我們決定仔細檢視它們,以找出哪些應該放入標準中。這需要回答幾個重要問題。為什麼巨集能運作得這麼好?為什麼人們會使用它們?
我們的假設是,這是因為 def 巨集中表達的難以理解的元程式設計概念,依賴於類型化方法呼叫的熟悉概念。由於這個原因,使用者撰寫的程式碼可以在不膨脹或失去可理解性的情況下吸收更多意義。
黑盒和白盒巨集
然而,有時 def 巨集會超越「只是一個一般方法」的概念。例如,巨集展開有可能產生比巨集回傳型別更具體的型別表達式。在 Scala 2.10 中,此類展開會保留其精確型別,正如 Stack Overflow 中〈Scala 巨集的靜態回傳型別〉一文中所強調的。
這個奇特的特性提供了額外的彈性,讓〈偽型別提供者〉、〈延伸的香草具象化〉、〈fundep 具象化〉和〈萃取器巨集〉成為可能,但它也犧牲了清晰度,無論對人類或機器而言皆然。
為了具體化在行為上就像一般方法的巨集和會精緻其回傳型別的巨集之間的關鍵區別,我們引入了黑盒巨集和白盒巨集的概念。忠實遵循其型別簽章的巨集稱為黑盒巨集,因為它們的實作與理解其行為無關(可以視為黑盒)。在 Scala 的型別系統中無法有精確簽章的巨集稱為白盒巨集(白盒 def 巨集確實有簽章,但這些簽章只是近似值)。
我們承認黑盒和白盒巨集都很重要,但我們對黑盒巨集更有信心,因為它們更容易解釋、指定和支援。因此,我們標準化巨集的計畫只包含黑盒巨集。稍後,我們也可能會將白盒巨集納入我們的計畫,但現在還言之過早。
編碼區別
在 2.11 發行版中,我們採取標準化的第一步,在 def 巨集的簽章中表達黑盒與白盒巨集之間的區別,以便 scalac
可以不同地處理此類巨集。這只是一個準備步驟,因此在 Scala 2.11 中黑盒和白盒巨集仍然是實驗性的。
我們通過用 scala.reflect.macros.blackbox.Context
和 scala.reflect.macros.whitebox.Context
替換 scala.reflect.macros.Context
來表達這種區別。如果巨集實作是以 blackbox.Context
作為其第一個引數定義的,則使用它的巨集定義被視為黑盒,類似的還有 whitebox.Context
。當然,原始的 Context
出於相容性原因仍然存在,但它會發出棄用警告,鼓勵在黑盒和白盒巨集中進行選擇。
黑盒 def 巨集的處理方式不同於 Scala 2.10 的 def 巨集。Scala 型別檢查器對它們套用以下限制
- 當黑盒巨集的應用程式擴充為樹狀結構
x
時,擴充會包裝到型別歸因(x: T)
中,其中T
是黑盒巨集的宣告回傳型別,其型別引數和路徑相依性與正在擴充的特定巨集應用程式一致。這使黑盒巨集作為 型別提供者 的實作載具失效。 - 當黑盒巨集應用程式在 Scala 型別推論演算法完成運作後仍有未確定的型別參數時,這些型別參數會被強制推論,其方式與一般方法的型別推論完全相同。這使得黑盒巨集無法影響型別推論,禁止 fundep 實體化。
- 當黑盒巨集應用程式用作隱含候選項時,在巨集被選為隱含搜尋結果之前,不會執行任何擴充。這使得無法 動態計算隱含巨集的可用性。
- 當黑盒巨集應用程式在模式比對中用作萃取器時,會觸發無條件的編譯器錯誤,防止使用巨集實作的模式比對自訂化。
白盒 def 巨集的運作方式與 Scala 2.10 中的 def 巨集完全相同。不會套用任何限制,因此在 2.10 中可以使用巨集完成的所有事情都可以在 2.11 和 2.12 中完成。