第 12 章 語言擴展

8 類型層級的模組別名

(在 OCaml 4.02 中引入)

規格::= ...
 module模組名稱=模組路徑

以上規格,在簽名內,僅匹配等於 模組路徑 的模組定義。相反地,類型層級的模組別名可以與自身匹配,或與它所引用的模組類型的任何超類型匹配。

對於 模組路徑 有幾個限制

  1. 它應該是 M0.M1...Mn 的形式 (,沒有函子應用);
  2. 在函子的主體內部,M0 不應該是函子的參數之一;
  3. 在遞迴模組定義內部,M0 不應該是遞迴定義的模組之一。

這樣的規格也會被推斷。也就是說,當 P 是一個滿足上述約束的路徑時,

module N = P

有類型

module N = P

類型層級的模組別名用於檢查模組路徑的相等性。也就是說,在模組名稱 N 已知是 P 的別名的情況下,不僅這兩個模組路徑檢查為相等,而且 F(N) 和 F(P) 也被認為是相等的。在預設編譯模式下,這與之前模組別名僅具有與其引用的模組相同的模組類型的方法的唯一區別。

當編譯器標誌 -no-alias-deps 啟用時,也會利用類型層級的模組別名來避免在編譯單元之間引入依賴關係。也就是說,引用另一個編譯單元內的模組的模組別名不會引入該編譯單元的連結時間依賴關係,只要它沒有被解引用即可;如果需要讀取介面,則仍然會引入編譯時間依賴關係,,如果模組是編譯單元的子模組,或如果引用了某些類型元件。此外,存取模組別名會在包含別名所引用的模組的編譯單元上引入連結時間依賴關係,而不是在包含別名的編譯單元上引入。請注意,連結時間行為中的這些差異可能與先前的行為不相容,因為某些編譯單元可能不會從程式庫中提取,並且它們的副作用可能會被忽略。

這些減弱的依賴關係使得可以使用模組別名來代替 -pack 機制。假設您有一個由模組 AB 組成的程式庫 Mylib。使用 -pack,您會發出命令列

ocamlc -pack a.cmo b.cmo -o mylib.cmo

並因此獲得一個 Mylib 編譯單元,其中物理上包含 AB 作為子模組,並且不依賴於它們各自的編譯單元。以下是一個可能的替代方法的具體範例

  1. 將包含 AB 的檔案重新命名為 Mylib__AMylib__B
  2. 建立一個包含以下行的包裝介面 Mylib.ml
    module A = Mylib__A
    module B = Mylib__B
    
  3. 使用 -no-alias-deps 編譯 Mylib.ml,並使用 -no-alias-deps-open Mylib 編譯其他檔案 (最後一個等效於在每個檔案的頂部添加 open! Mylib 行)。
    ocamlc -c -no-alias-deps Mylib.ml
    ocamlc -c -no-alias-deps -open Mylib Mylib__*.mli Mylib__*.ml
    
  4. 最後,建立一個包含所有編譯單元的程式庫,並匯出所有已編譯的介面。
    ocamlc -a Mylib*.cmo -o Mylib.cma
    

這種方法可讓您直接在程式庫內部存取 AB,並從外部以 Mylib.AMylib.B 的形式存取。它還有一個優點是 Mylib 不再是單體的:如果您使用 Mylib.A,則只會連結 Mylib__A,而不會連結 Mylib__B

請注意 Mylib__AMylib__B 中雙底線的使用。這些是有意選擇的;編譯器在列印路徑時使用以下啟發式方法:給定一個路徑 Lib__fooBar,如果 Lib.FooBar 存在並且是 Lib__fooBar 的別名,則編譯器將始終顯示 Lib.FooBar 而不是 Lib__fooBar。這樣,長的 Mylib__ 名稱會保持隱藏,而使用者看到的只是更優雅的點名稱。這就是 OCaml 標準程式庫的編譯方式。