第 12 章 語言擴展

13 擴展節點

(在 OCaml 4.02 中引入,4.03 中加入表達式以外的結構的中綴符號,4.04 中加入中綴符號 (e1 ;%ext e2)。)

擴展節點是語法樹中的通用佔位符。它們會被型別檢查器拒絕,並旨在由外部工具(例如 -ppx 重寫器)「擴展」。

擴展節點與屬性 ‍12.12 共享相同的識別符號和酬載概念。

第一種形式的擴展節點用於「代數」類別

擴展::= [%attr-idattr-payload]
 
expr::= ...
 擴展
 
typexpr::= ...
 擴展
 
pattern::= ...
 擴展
 
module-expr::= ...
 擴展
 
module-type::= ...
 擴展
 
class-expr::= ...
 擴展
 
class-type::= ...
 擴展
 

第二種形式的擴展節點可用於結構和簽章中,無論是在模組還是物件語言中

item-extension::= [%%attr-idattr-payload]
 
definition::= ...
 item-extension
 
specification::= ...
 item-extension
 
class-field-spec::= ...
 item-extension
 
class-field::= ...
 item-extension
 

當酬載具有相同的類型時(表達式與表達式、模式與模式 ...),可以使用擴展節點的中綴形式。

範例

let%foo x = 2 in x + 1     === [%foo let x = 2 in x + 1]
begin%foo ... end          === [%foo begin ... end]
x ;%foo 2                  === [%foo x; 2]
module%foo M = ..          === [%%foo module M = ... ]
val%foo x : t              === [%%foo: val x : t]

當此形式與屬性的中綴語法一起使用時,這些屬性將被視為應用於酬載

fun%foo[@bar] x -> x + 1 === [%foo (fun x -> x + 1)[@bar ] ];

當擴展節點用於實現綁定運算符時,為了方便起見,可以使用額外的簡寫形式 let%foo x in ...(請參閱 12.23.4)。

此外,引號字串 {|...|} 可以與擴展節點組合,以嵌入外部語法片段。這些片段可以由預處理器解釋,並轉換為 OCaml 程式碼,而無需轉義引號。它們可以使用語法捷徑

{%%foo|...|}               === [%%foo{|...|}]
let x = {%foo|...|}        === let x = [%foo{|...|}]
let y = {%foo bar|...|bar} === let y = [%foo{bar|...|bar}]

例如,您可以假設您有一個識別 %sql 擴展的 ppx-rewriter,使用 {%sql|...|} 來表示任意 SQL 敘述。

請注意,以單字分隔的形式,例如 {sql|...|sql},不應使用於表示正在使用擴展。實際上,使用者無法從程式碼中看出此字串常值的語義是否與他們的期望不同。此外,為特定分隔符號賦予語義會限制更改分隔符號以避免轉義問題的自由。

13.1 內建擴展節點

(在 OCaml 4.03 中引入)

編譯器本身理解一些擴展節點

type t = .. type t += X of int | Y of string let x = [%extension_constructor X] let y = [%extension_constructor Y]
# x <> y;;
- : bool = true