第 12 章 語言擴展

3 私有型別

模組簽章中的私有型別宣告,形式為 type t = private ...,使程式庫能夠向程式庫的客戶端揭示型別實作的某些方面,但並非全部。在這方面,它們在抽象型別宣告(其中沒有揭示有關型別實作的任何資訊)與資料型別定義和型別縮寫(其中公開了型別實作的所有方面)之間取得了中間立場。私有型別宣告有三種形式:用於變體和記錄型別(第 12.3.1 節)、用於型別縮寫(第 12.3.2 節)以及用於列型別(第 12.3.3 節)。

3.1 私有變體和記錄型別

(在 Objective Caml 3.07 中引入)

型別表示法::= ...
 =private [ | ] constr-decl { |constr-decl }
 =privaterecord-decl

宣告為 private 的變體或記錄型別的值,可以在模式比對中或透過 expr . field 符號(用於記錄存取)中正常解構。但是,這些型別的值無法透過建構子應用或記錄建構直接建構。此外,不允許對私有記錄型別的可變欄位進行指派。

私有型別的典型用法是在模組的匯出簽章中,以確保私有型別的值的建構始終透過模組提供的函式進行,同時仍然允許在定義模組外部進行模式比對。例如

module M : sig type t = private A | B of int val a : t val b : int -> t end = struct type t = A | B of int let a = A let b n = assert (n > 0); B n end

在這裡,private 宣告確保任何 M.t 型別的值中,B 建構子的引數始終為正整數。

關於其參數的變異數,私有型別的處理方式與抽象型別相同。也就是說,如果私有型別具有參數,則其變異數是由在參數前面加上「+」或「-」明確給定的,否則為不變異數。

3.2 私有型別縮寫

(在 Objective Caml 3.11 中引入)

型別方程式::= ...
 =privatetypexpr

與常規型別縮寫不同,私有型別縮寫宣告的型別與其實作型別 typexpr 不同。但是,允許從型別到 typexpr 的強制轉換。此外,編譯器「知道」實作型別,並且可以利用此知識來執行型別導向的優化。

以下範例使用私有型別縮寫來定義非負整數的模組

module N : sig type t = private int val of_int: int -> t val to_int: t -> int end = struct type t = int let of_int n = assert (n >= 0); n let to_int n = n end

N.t 型別與 int 不相容,確保非負整數與常規整數不會混淆。但是,如果 x 的型別為 N.t,則強制轉換 (x :> int) 是合法的,並傳回基礎整數,就像 N.to_int x 一樣。也支援深度強制轉換:如果 l 的型別為 N.t list,則強制轉換 (l :> int list) 傳回基礎整數的列表,就像 List.map N.to_int l 一樣,但不會複製列表 l

請注意,強制轉換 ( expr :> typexpr ) 實際上是一種縮寫形式,只有在存在私有縮寫時才有效,如果 expr 的型別或 typexpr 都不包含任何型別變數。如果它們包含型別變數,則必須使用完整形式 ( expr : typexpr1 :> typexpr2 ),其中 typexpr1expr 的預期型別。具體來說,對於以上範例,這將是 (x : N.t :> int)(l : N.t list :> int list)

3.3 私有列型別

(在 Objective Caml 3.09 中引入)

型別方程式::= ...
 =privatetypexpr

私有列型別是型別縮寫,其中型別的部分結構保留為抽象。具體來說,上述的 typexpr 應該表示物件型別或多型變體型別,並保留一些精煉的可能性。如果在介面中使用私有宣告,則對應的實作可以提供基礎實例,或精煉的私有型別。

module M : sig type c = private < x : int; .. > val o : c end = struct class c = object method x = 3 method y = 2 end let o = new c end

此宣告不僅隱藏了 y 方法,還使 c 型別與任何其他封閉的物件型別不相容,這表示只有 o 的型別為 c。在這方面,它的行為類似於私有記錄型別。但是,私有列型別在增量精煉方面更具彈性。此功能可以與函子組合使用。

module F(X : sig type c = private < x : int; .. > end) = struct let get_x (o : X.c) = o#x end module G(X : sig type c = private < x : int; y : int; .. > end) = struct include F(X) let get_y (o : X.c) = o#y end

例如,多型變體型別 [t]

type t = [ `A of int | `B of bool ]

可以透過兩種方式精煉。[u] 的定義可以在 [t] 中新增欄位,並且宣告

type u = private [> t]

將使這些新欄位保持抽象。可以使用 [t] 的已知變體來建構型別 [u] 的值,但是任何模式比對都需要一個預設案例來處理潛在的額外欄位。相反,宣告 [u] 可以透過抽象來限制 [t] 的欄位:宣告

type v = private [< t > `A]

對應於私有變體型別。除了使用明確列出的存在的建構子(在此範例中為 (`A n))外,無法建立私有型別 [v] 的值;但是,在對 [v] 進行模式比對時,應假設 [t] 的任何建構子都可能存在。

與抽象型別類似,不會推斷型別參數的變異數,必須明確給定。