第 11 章 OCaml 语言

10 模块类型(模块规范)

模块类型是模块级别的类型表达式的等价物:它们指定模块的一般形状和类型属性。

模块类型::= 模块类型路径
 sig { 规范 [;;] } end
 functor(模块名:模块类型)->模块类型
 模块类型->模块类型
 模块类型with模块约束 { and模块约束 }
 (模块类型)
 
模块约束::= type [类型参数] 类型构造器类型等式 { 类型约束 }
 module模块路径=扩展模块路径
 
规范::= val值名:类型表达式
 external值名:类型表达式=外部声明
 类型定义
 exception构造器声明
 类规范
 类类型定义
 module模块名:模块类型
 module模块名 { (模块名:模块类型) } :模块类型
 moduletype模块类型名
 moduletype模块类型名=模块类型
 open模块路径
 include模块类型

另请参阅以下语言扩展:恢复模块的类型签名内的替换类型级模块别名属性扩展节点生成函数子模块类型替换

10.1 简单模块类型

表达式 模块类型路径 等价于绑定到名称 模块类型路径 的模块类型。表达式 ( 模块类型 ) 表示与 模块类型 相同的类型。

10.2 签名

签名是结构的类型规范。签名 sigend 是值名称、类型名称、异常、模块名称和模块类型名称的类型规范的集合。如果结构为签名中指定的所有名称(以及可能更多名称)提供定义(实现),并且这些定义满足签名中给出的类型要求,则该结构将与签名匹配。

在签名的每个规范之后都允许使用可选的 ;;。它用作没有语义含义的语法分隔符。

值规范

签名中值组件的规范写成 val 值名 : 类型表达式,其中 值名 是值的名,类型表达式 是其预期类型。

形式 external 值名 : 类型表达式 = 外部声明 类似,除了它还需要将名称实现为 外部声明 中指定的外部函数(请参阅第 ‍22 章)。

类型规范

签名中一个或多个类型组件的规范写成 type 类型定义 { and 类型定义 },由一系列类型名称的互递归定义组成。

签名中的每个类型定义都指定了一个可选的类型等式 = 类型表达式 和一个可选的类型表示 = 构造器声明 … 或 = { 字段声明}。匹配结构中类型名称的实现必须与等式中指定的类型表达式兼容(如果给出),并且具有指定的表示(如果给出)。反过来,该签名的用户将能够依赖于类型等式或类型表示(如果给出)。更准确地说,我们有以下四种情况

抽象类型:没有等式,没有表示。
 ‍
在签名中定义为抽象类型的名称可以在匹配的结构中由任何类型的类型定义实现(前提是它具有相同数量的类型参数)。类型的精确实现将对结构的用户隐藏。特别是,如果类型实现为变体类型或记录类型,则关联的构造函数和字段将无法被用户访问;如果类型实现为缩写,则类型名称与缩写右侧之间的类型相等性将对结构的用户隐藏。结构的用户认为该类型与任何其他类型都不兼容:已生成一个新的类型。
类型缩写:等式 = 类型表达式,没有表示。
 ‍
类型名称必须由与 类型表达式 兼容的类型实现。结构的所有用户都知道类型名称与 类型表达式 兼容。
新的变体类型或记录类型:没有等式,有表示。
 ‍
类型名称必须由具有精确指定的构造函数或字段的变体类型或记录类型实现。结构的所有用户都可以访问构造函数或字段,并可以使用它们来创建或检查该类型的值。但是,结构的用户认为该类型与任何其他类型都不兼容:已生成一个新的类型。
重新导出的变体类型或记录类型:有等式,有表示。
 ‍
这种情况结合了前两种情况:类型的表示对所有用户可见,并且不会生成新的类型。

异常规范

签名中的规范 exception 构造器声明 要求匹配的结构提供一个具有定义中指定的名称和参数的异常,并使异常可供结构的所有用户使用。

类规范

签名中一个或多个类的规范写成 class 类规范 { and 类规范 },由一系列类名称的互递归定义组成。

第 ‍11.9.4 节更精确地描述了类规范。

类类型规范

签名中一个或多个类类型的规范写成 class type classtype-def { and classtype-def },它由一系列类类型名称的互递归定义组成。类类型规范在第 ‍11.9.5 节中进行了更精确的描述。

模块规范

签名中模块组件的规范写成 module module-name : module-type,其中 module-name 是模块组件的名称,module-type 是其预期类型。模块可以任意嵌套;特别是,函子可以作为结构的组件出现,函子类型可以作为签名的组件出现。

为了指定作为函子的模块组件,可以写成

module module-name ( name1 : module-type1 )( namen : module-typen ) : module-type

而不是

module module-name : functor ( name1 : module-type1 ) ->-> module-type

模块类型规范

签名的模块类型组件可以指定为显式模块类型或抽象模块类型。

抽象模块类型规范 module type modtype-name 允许名称 modtype-name 由匹配签名中的任何模块类型实现,但对所有签名用户隐藏模块类型的实现。

显式模块类型规范 module type modtype-name = module-type 要求名称 modtype-name 由匹配签名中的模块类型 module-type 实现,但使 modtype-namemodule-type 之间的相等性对所有签名用户可见。

打开模块路径

签名中的表达式 open module-path 不指定任何组件。它只是影响签名中后续项目的解析,允许通过其简单名称 name 来引用由 module-path 表示的模块的组件,而不是路径访问 module-path . nameopen 的作用域在签名表达式的末尾结束。

包含签名

签名中的表达式 include module-type 执行由 module-type 表示的签名的组件的文本包含。它的行为就像包含签名的组件被复制到 include 的位置一样。 module-type 参数必须引用一个作为签名的模块类型,而不是函子类型。

10.3 函子类型

模块类型表达式 functor ( module-name : module-type1 ) -> module-type2 是函子(从模块到模块的函数)的类型,它以类型为 module-type1 的模块作为参数,并以类型为 module-type2 的模块作为结果。模块类型 module-type2 可以使用名称 module-name 来引用函子实际参数的类型组件。如果类型 module-type2 不依赖于 module-name 的类型组件,则可以使用备选的简短语法 module-type1 -> module-type2 来简化模块类型表达式。对函子参数的类型没有限制;特别是,函子可以接受另一个函子作为参数(“高阶”函子)。

当结果模块类型本身是函子时,

functor ( name1 : module-type1 ) ->-> functor ( namen : module-typen ) -> module-type

可以使用简写形式

functor ( name1 : module-type1 )( namen : module-typen ) -> module-type

10.4 with 运算符

假设 module-type 表示一个签名,则表达式 module-type with mod-constraint { and mod-constraint } 表示同一个签名,其中类型等式已添加到某些类型规范中,如 with 关键字后面的约束所描述。约束 type [type-parameters] typeconstr = typexpr 将类型等式 = typexpr 添加到受约束签名的名为 typeconstr 的类型组件的规范中。约束 module module-path = extended-module-path 将类型等式添加到由 module-path 表示的子结构的所有类型组件中,使它们等效于由 extended-module-path 表示的结构的相应类型组件。

例如,如果模块类型名称 S 绑定到签名

        sig type t module M: (sig type u end) end

那么 S with type t=int 表示签名

        sig type t=int module M: (sig type u end) end

S with module M = N 表示签名

        sig type t module M: (sig type u=N.u end) end

一个接受两个类型为 S 且共享其 t 组件的参数的函子写成

        functor (A: S) (B: S with type t = A.t) ...

约束从左到右添加。在应用每个约束后,结果签名必须是应用约束之前的签名的子类型。因此, with 运算符只能在签名的类型组件上添加信息,而不能删除信息。