第 11 章 OCaml 语言

8 类型和异常定义

8.1 类型定义

类型定义将类型构造器绑定到数据类型:变体类型、记录类型、类型缩写或抽象数据类型。它们还绑定与定义关联的值构造器和记录字段。

type-definition::= type [nonrec] typedef { andtypedef }
 
typedef::=[type-params] typeconstr-nametype-information
 
type-information::=[type-equation] [type-representation] { type-constraint }
 
type-equation::= =typexpr
 
type-representation::= = [|] constr-decl { |constr-decl }
 =record-decl
 =|
 
type-params::= type-param
 (type-param { ,type-param } )
 
type-param::=[ext-variance] 'ident
 
ext-variance::= variance [injectivity]
 injectivity [variance]
 
variance::= +
 -
 
injectivity::=!
 
record-decl::= {field-decl { ;field-decl } [;] }
 
constr-decl::=(constr-name ∣ [] ∣ (::)) [ ofconstr-args ]
 
constr-args::= typexpr { *typexpr }
 
field-decl::=[mutable] field-name:poly-typexpr
 
type-constraint::= constrainttypexpr=typexpr

另请参见以下语言扩展:私有类型广义代数数据类型属性扩展节点可扩展变体类型内联记录

类型定义由 type 关键字引入,并由一个或多个简单的定义组成,这些定义可能是相互递归的,并由 and 关键字分隔。每个简单定义都定义一个类型构造器。

一个简单定义由一个小写标识符组成,前面可能带有一个或多个类型参数,后面跟有一个可选的类型等式,然后是一个可选的类型表示,最后是一个约束子句。标识符是被定义的类型构造器的名称。

type colour =
  | Red | Green | Blue | Yellow | Black | White
  | RGB of {r : int; g : int; b : int}

type 'a tree = Lf | Br of 'a * 'a tree * 'a;;

type t = {decoration : string; substance : t'}
and t' = Int of int | List of t list

在类型定义的右侧,对其中一个被定义的类型构造器名称的引用被视为递归,除非 type 后面跟着 nonrecnonrec 关键字是在 OCaml 4.02.2 中引入的。

可选的类型参数可以是单个类型变量 ' ident(对于带有一个参数的类型构造器),或者是一个类型变量列表 ('ident1,…,'identn)(对于带有多个参数的类型构造器)。每个类型参数都可以以一个方差约束 +(分别为 -)为前缀,表示该参数是协变的(分别为逆变的),以及一个注入性注释 !,表示该参数可以从整个类型中推断出来。这些类型参数可以出现在定义右侧的类型表达式中,也可以通过方差约束来限制;例如,协变参数只能出现在函数箭头的右侧(更准确地说,遵循偶数个箭头的左侧分支),而逆变参数只能出现在左侧(奇数个箭头的左侧分支)。如果类型具有表示或等式,并且参数是自由的(未通过类型约束绑定到构造类型),则会检查其方差约束,但子类型将使用参数的推断方差,这可能不那么严格;否则(对于抽象类型或非自由参数),必须显式给出方差,如果未给出方差,则参数为不变的。

可选的类型等式 = typexpr 使定义的类型等价于类型表达式 typexpr:在类型检查期间可以将一个替换为另一个。如果未给出类型等式,则会生成一个新类型:定义的类型与任何其他类型都不兼容。

可选的类型表示通过给出关联的构造器列表(如果是变体类型)或关联的字段列表(如果是记录类型)来描述表示定义类型的数​​据结构。如果未给出类型表示,则除了可选类型等式中声明的内容外,不会对类型的结构做出任何假设。

类型表示 = [|] constr-decl { | constr-decl } 描述了一个变体类型。构造器声明 constr-decl1, …, constr-decln 描述与该变体类型关联的构造器。构造器声明 constr-name of typexpr1 ** typexprn 将名称 constr-name 声明为非常量构造器,其参数的类型为 typexpr1typexprn。构造器声明 constr-name 将名称 constr-name 声明为常量构造器。构造器名称必须大写。

类型表示 = { field-decl { ; field-decl } [;] } 描述了一个记录类型。字段声明 field-decl1, …, field-decln 描述与该记录类型关联的字段。字段声明 field-name : poly-typexprfield-name 声明为一个字段,其参数的类型为 poly-typexpr。字段声明 mutable field-name : poly-typexpr 的行为类似;此外,它允许对该字段进行物理修改。不可变字段是协变的,可变字段是不变的。可变和不可变字段都可以具有显式多态类型。每当创建或修改记录值时,都会静态检查内容的多态性。提取的值可以实例化其类型。

类型定义的两个组成部分,可选等式和可选表示,可以独立组合,产生四种典型情况

抽象类型:无等式,无表示。
 ‍
当出现在模块签名中时,此定义除了参数数量外,对类型构造函数没有任何说明:其表示形式是隐藏的,并且假定与任何其他类型不兼容。
类型缩写:一个等式,没有表示形式。
 ‍
这将类型构造函数定义为等号右侧类型表达式的缩写。
新的变体类型或记录类型:没有等式,有一个表示形式。
 ‍
这会生成一个新的类型构造函数,并定义相关的构造函数或字段,通过这些构造函数或字段可以直接构建或检查该类型的值。
重新导出的变体类型或记录类型:一个等式,一个表示形式。
 ‍
在这种情况下,类型构造函数被定义为等式中给定类型表达式的缩写,但此外,表示形式中给出的构造函数或字段仍然附加到定义的类型构造函数上。等式部分中的类型表达式必须与表示形式一致:它必须是相同类型(记录或变体),并且具有完全相同的构造函数或字段,顺序相同,参数相同。此外,新的类型构造函数必须与原始类型构造函数具有相同的元数和相同的类型约束。

作为类型参数出现的类型变量可以选择以+-为前缀,以指示类型构造函数相对于此参数是协变还是逆变。此方差信息用于在检查:>强制转换的有效性时确定子类型关系(请参见第11.7.7节)。

例如,type +'a t声明t为一个抽象类型,在其参数中是协变的;这意味着如果类型τ是类型σ的子类型,则τ t是σ t的子类型。类似地,type -'a t声明抽象类型t在其参数中是逆变的:如果τ是σ的子类型,则σ t是τ t的子类型。如果没有给出+-方差注释,则假定类型构造函数在相应参数中是非变的。例如,抽象类型声明type 'a t表示如果τ是σ的子类型,则τ t既不是σ t的子类型也不是超类型。

参数上+-注释指示的方差仅对抽象类型和私有类型强制执行,或者当存在类型约束时强制执行。否则,对于缩写、没有类型约束的变体和记录类型,类型构造函数的方差属性是从其定义推断出来的,并且方差注释仅检查是否符合定义。

注入性注释仅对抽象类型和私有行类型必要,因为否则可以从类型声明中推导出它们:所有参数对于记录和变体类型声明(包括可扩展类型)都是注入的;对于类型缩写,如果参数在其定义等式中具有注入性出现(无论是私有的还是非私有的),则该参数是注入性的。对于类型缩写中的受约束类型参数,如果它们出现在主体中的注入性位置,或者如果它们的类型变量都是注入性的,则它们是注入性的;特别是,如果受约束类型参数包含一个未出现在主体中的变量,则它不可能是注入性的。

构造constraint ' ident = typexpr允许指定类型参数。对应于类型参数ident的任何实际类型参数都必须是typexpr的实例(更准确地说,identtypexpr被统一)。typexpr的类型变量可以出现在类型等式和类型声明中。

8.2 异常定义

异常定义::= exceptionconstr-decl
 exceptionconstr-name=constr

异常定义向内置的异常值变体类型exn添加新的构造函数。构造函数的声明方式与变体类型的定义相同。

# exception E of int * string;;
exception E of int * string

表单exception constr-decl生成一个新的异常,与系统中的所有其他异常不同。表单exception constr-name = constr为现有异常提供一个替代名称。

# exception E of int * string exception F = E let eq = E (1, "one") = F (1, "one");;
exception E of int * string exception F of int * string val eq : bool = true