val store : '_weak1 option ref = {contents = None}
由于 None 的类型是 'a option,而函数 ref 的类型是 'b -> 'b ref,因此 store 类型的一个自然推论是 'a option ref。但是,推断出的类型 '_weak1 option ref 却有所不同。类型变量名称以 _weak 前缀开头(例如 '_weak1)的类型变量是弱多态类型变量,有时简称为“弱类型变量”。弱类型变量是当前未知的单个类型的占位符。一旦占位符类型 '_weak1 后面的特定类型 t 已知,所有 '_weak1 的出现都将被 t 替换。例如,我们可以定义另一个选项引用并在其中存储一个 int
#let another_store = ref None ;;
val another_store : '_weak2 option ref = {contents = None}
# another_store := Some 0; another_store ;;
- : int option ref = {contents = Some 0}
在 another_store 中存储一个 int 之后,another_store 的类型已从 '_weak2 option ref 更新为 int option ref。弱多态类型变量和通用多态类型变量之间的这种区别可以保护 OCaml 程序免受不安全性和运行时错误的影响。要了解不安全性可能来自哪里,请考虑这个简单的函数,它在存在的情况下,将值 x 与存储在 store 引用中的值交换
#let swap store x = match !store with | None -> store := Some x; x | Some y -> store := Some x; y;;
val swap : 'a option ref -> 'a -> 'a = <fun>
我们可以将此函数应用于我们的存储
#let one = swap store 1 let one_again = swap store 2 let two = swap store 3;;
val one : int = 1 val one_again : int = 1 val two : int = 2
方差描述了类型构造函数相对于子类型化的行为。例如,考虑一对类型 x 和 xy,其中 x 是 xy 的子类型,表示为 x :> xy
#type x = [ `X ];;
type x = [ `X ]
#type xy = [ `X | `Y ];;
type xy = [ `X | `Y ]
由于 x 是 xy 的子类型,因此我们可以将类型 x 的值转换为类型 xy 的值
#let x:x = `X;;
val x : x = `X
#let x' = ( x :> xy);;
val x' : xy = `X
类似地,如果我们有一个类型为 x list 的值,我们可以将其转换为类型为 xy list 的值,因为我们可以逐个转换每个元素
#let l:x list = [`X; `X];;
val l : x list = [`X; `X]
#let l' = ( l :> xy list);;
val l' : xy list = [`X; `X]
换句话说,x :> xy 意味着 x list :> xy list,因此类型构造函数 'a list 在其参数 'a 中是协变的(它保留了子类型化)。
相反,如果我们有一个可以处理类型为 xy 的值的函数
#let f: xy -> unit = function | `X -> () | `Y -> ();;
val f : xy -> unit = <fun>
它也可以处理类型为 x 的值
#let f' = (f :> x -> unit);;
val f' : x -> unit = <fun>
请注意,我们可以将 f 和 f' 的类型重写为
#type 'a proc = 'a -> unit let f' = (f: xy proc :> x proc);;
type 'a proc = 'a -> unit val f' : x proc = <fun>
在这种情况下,我们有 x :> xy 意味着 xy proc :> x proc。请注意,第二个子类型关系反转了 x 和 xy 的顺序:类型构造函数 'a proc 在其参数 'a 中是逆变的。更一般地,函数类型构造函数 'a -> 'b 在其返回类型 'b 中是协变的,在其参数类型 'a 中是逆变的。
类型构造函数在其某些类型参数中也可以是不变的,既不是协变也不是逆变。一个典型的例子是引用
#let x: x ref = ref `X;;
val x : x ref = {contents = `X}
如果我们能够将 x 作为变量 xy 转换为类型 xy ref,我们就可以使用 xy 在引用中存储值 `Y,然后使用 x 值将此内容读取为类型 x 的值,这将破坏类型系统。
#type 'a regular_nested = List of 'a list | Nested of 'a regular_nested list let l = Nested[ List [1]; Nested [List[2;3]]; Nested[Nested[]] ];;
type 'a regular_nested = List of 'a list | Nested of 'a regular_nested list val l : int regular_nested = Nested [List [1]; Nested [List [2; 3]]; Nested [Nested []]]
请注意,类型构造函数 regular_nested 在上面的定义中始终显示为 'a regular_nested,使用相同的参数 'a。使用此类型,我们可以使用经典的递归函数计算最大深度
#letrec maximal_depth = function | List _ -> 1 | Nested [] -> 0 | Nested (a::q) -> 1 + max (maximal_depth a) (maximal_depth (Nested q));;
val maximal_depth : 'a regular_nested -> int = <fun>
#let len nested = let map_and_sum f = List.fold_left (fun acc x -> acc + f x) 0 inletrec len: 'a. ('a list -> int ) -> 'a nested -> int = fun nested_len n -> match n with | List l -> nested_len l | Nested n -> len (map_and_sum nested_len) n in len List.length nested;;
#let shape n = letrec shape: 'a 'b. ('a nested -> int nested) -> ('b list list -> 'a list) -> 'b nested -> int nested = fun nest nested_shape -> function | List l -> raise (Invalid_argument "shape requires nested_list of depth greater than 1") | Nested (List l) -> nest @@ List (nested_shape l) | Nested n -> let nested_shape = List.map nested_shape inlet nest x = nest (Nested x) in shape nest nested_shape n in shape (fun n -> n ) (fun l -> List.map List.length l ) n;;