第 12 章 语言扩展

3 私有类型

模块签名中的私有类型声明,形式为 type t = private ...,允许库向客户端公开类型实现的某些方面,但并非全部。在这方面,它们介于抽象类型声明(不公开类型实现的任何信息)和数据类型定义和类型缩写(公开类型实现的所有方面)之间。私有类型声明有三种形式:用于变体和记录类型(第 ‍12.3.1 节),用于类型缩写(第 ‍12.3.2 节)和用于行类型(第 ‍12.3.3 节)。

3.1 私有变体和记录类型

(在 Objective Caml 3.07 中引入)

type-representation::= ...
 =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 中引入)

type-equation::= ...
 =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.tint 不兼容,确保非负整数和常规整数不会混淆。但是,如果 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 中引入)

type-equation::= ...
 =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] 的任何构造函数都可能存在。

与抽象类型类似,类型参数的方差不会推断,必须显式给出。