第 12 章 语言扩展

22 广义 open 语句

(在 4.08 中引入)

定义::= ...
   open  module-expr
   open!module-expr
 
规范::= ...
   open  extended-module-path
   open!extended-module-path
 
表达式::= ...
 letopen  module-exprinexpr
 letopen!module-exprinexpr
 

此扩展允许在模块结构和表达式中打开任何模块表达式。类似的机制也适用于模块类型内部,但仅限于扩展模块路径(例如 F(X).G(Y))。

例如,模块可以在打开时进行约束

module M = struct let x = 0 let hidden = 1 end open (M:sig val x: int end) let y = hidden
错误: 未绑定的值 hidden

另一种可能性是立即打开函子应用的结果

let sort (type x) (x:x list) = let open Set.Make(struct type t = x let compare=compare end) in elements (of_list x)
val sort : 'x list -> 'x list = <fun>

更进一步,这种构造可以在结构内部引入局部组件,

module M = struct let x = 0 open! struct let x = 0 let y = 1 end let w = x + y end
module M : sig val x : int val w : int end

一个重要的限制是,由 open struct ... end 引入的类型不能出现在封闭结构的签名中,除非它们被定义为等于某个非局部类型。所以

module M = struct open struct type 'a t = 'a option = None | Some of 'a end let x : int t = Some 1 end
module M : sig val x : int option end

是可以的,但是

module M = struct open struct type t = A end let x = A end
错误: 此 open 引入的类型 t 出现在签名中。如果 t 被隐藏,则值 x 没有有效的类型。

是不行的,因为 x 无法被赋予除 t 之外的任何类型,而 t 仅在本地存在。虽然如果 x 也在本地,上面的代码会是 OK 的

module M: sig end = struct open struct type t = A endopen struct let x = A endend
module M : sig end

在签名内部,扩展 open 被限制为扩展模块路径,

module type S = sig module F: sig end -> sig type t end module X: sig end open F(X) val f: t end
module type S = sig module F : sig end -> sig type t end module X : sig end val f : F(X).t end

而不是

  open struct type t = int end

在这些情况下,可以使用局部替换(参见 12.7.2)。

注意,此扩展在类定义内部不可用

class c =
  let open Set.Make(Int) in
  ...