第 11 章 OCaml 语言

11 模块表达式(模块实现)

模块表达式是值表达式的模块级等效项:它们计算结果为模块,从而为模块类型中表达的规范提供实现。

module-expr::= module-path
 struct [ module-items ] end
 functor(module-name:module-type)->module-expr
 module-expr(module-expr)
 (module-expr)
 (module-expr:module-type)
 
module-items::={ ;; } ( definition ∣ expr ) { { ;; } ( definition ∣ ;;expr) } { ;; }
 
definition::= let [rec] let-binding { andlet-binding }
 externalvalue-name:typexpr=external-declaration
 type-definition
 exception-definition
 class-definition
 classtype-definition
 modulemodule-name { (module-name:module-type) } [ :module-type ]  =module-expr
 moduletypemodtype-name=module-type
 openmodule-path
 includemodule-expr

另请参见以下语言扩展:递归模块一等模块open 语句中的覆盖属性扩展节点生成性函子

11.1 简单模块表达式

表达式 module-path 计算结果为绑定到名称 module-path 的模块。

表达式 ( module-expr ) 计算结果与 module-expr 相同。

表达式 ( module-expr : module-type ) 检查 module-expr 的类型是否为 module-type 的子类型,也就是说,module-type 中指定的所有组件都在 module-expr 中实现,并且它们的实现满足 module-type 中给出的要求。换句话说,它检查实现 module-expr 是否满足类型规范 module-type。整个表达式计算结果与 module-expr 相同,只是 module-type 中未指定的所有组件都将被隐藏,并且无法再访问它们。

11.2 结构体

结构体 structend 是值名称、类型名称、异常、模块名称和模块类型名称定义的集合。定义按它们在结构体中出现的顺序计算。由定义执行的绑定的作用域扩展到结构体的末尾。因此,一个定义可以引用同一结构体中更早定义绑定到的名称。

为了与顶层短语(第 14 章)兼容,在结构体中每个定义之后和之前允许使用可选的 ;;。这些 ;; 没有语义意义。类似地,以 ;; 开头的 expr 允许作为结构体的组件。它等效于 let _ = expr,即 expr 为了其副作用而计算,但没有绑定到任何标识符。如果 expr 是结构体的第一个组件,则前面的 ;; 可以省略。

值定义

值定义 let [rec] let-binding { and let-binding } 以与 letin … 表达式(参见第 11.7.2 节)相同的方式绑定值名称。绑定左侧出现的名称绑定到右侧对应的值。

值定义 external value-name : typexpr = external-declarationvalue-name 实现为 external-declaration 中指定的外部函数(参见第 22 章)。

类型定义

一个或多个类型组件的定义写成 type typedef { and typedef },它包含一系列类型名称的相互递归定义。

异常定义

异常通过语法 exception constr-declexception constr-name = constr 定义。

类定义

一个或多个类的定义写成 class class-binding { and class-binding },它包含一系列类名称的相互递归定义。类定义在第 11.9.3 节中更详细地描述。

类类型定义

一个或多个类的定义写成 class type classtype-def { and classtype-def },它包含一系列类类型名称的相互递归定义。类类型定义在第 11.9.5 节中更详细地描述。

模块定义

定义模块组件的基本形式是 module module-name = module-expr,它计算 module-expr 并将结果绑定到名称 module-name

可以写

而不是

另一种派生形式是

模块 模块名 ( 名称1 : 模块类型1 )( 名称n : 模块类型n ) = 模块表达式

等价于

模块 模块名 = 函子 ( 名称1 : 模块类型1 ) ->-> 模块表达式

模块类型定义

模块类型的定义写成 模块 类型 模块类型名 = 模块类型。它将名称 模块类型名 绑定到表达式 模块类型 所表示的模块类型。

打开模块路径

表达式 打开 模块路径 在结构体中不会定义任何组件,也不会执行任何绑定。它只是影响结构体中后续项目的解析,允许由 模块路径 表示的模块的组件以它们的简单名称 名称 代替路径访问 模块路径 . 名称 来引用。打开 的作用域在结构体表达式的末尾结束。

包含另一个结构体的组件

表达式 包含 模块表达式 在结构体中会将由 模块表达式 表示的结构体中的所有定义重新导出到当前结构体。例如,如果你定义了一个模块 S,如下所示

模块 S = 结构 类型 t = 整数 x = 2 结束

定义模块 B

模块 B = 结构 包含 S y = (x + 1 : t) 结束

等价于将其定义为

模块 B = 结构 类型 t = S.t x = S.x y = (x + 1 : t) 结束

打开包含 之间的区别在于,打开 只是为打开的结构体的组件提供简短的名称,而不会定义当前结构体的任何组件,而 包含 还会为包含的结构体的组件添加定义。

11.3 函子

函子定义

表达式 函子 ( 模块名 : 模块类型 ) -> 模块表达式 计算为一个函子,它接收类型为 模块类型1 的模块作为参数,将 模块名 绑定到这些模块,在扩展的环境中计算 模块表达式,并将生成的模块作为结果返回。对函子参数的类型没有限制;特别是,函子可以接收另一个函子作为参数(“高阶”函子)。

当结果模块表达式本身是一个函子时,

函子 ( 名称1 : 模块类型1 ) ->-> 函子 ( 名称n : 模块类型n ) -> 模块表达式

可以使用简写形式

函子 ( 名称1 : 模块类型1 )( 名称n : 模块类型n ) -> 模块表达式

函子应用

表达式 模块表达式1 ( 模块表达式2 ) 计算 模块表达式1 为一个函子,并将 模块表达式2 计算为一个模块,并将前者应用于后者。 模块表达式2 的类型必须与函子 模块表达式1 所期望的参数类型匹配。