在 OCaml 中,一个用冒号声明其标签的变体
In OCaml, a variant declaring its tags with a colon
查看以下代码:
type z = Z of z
type 'a s = Z | S of 'a
type _ t = Z : z t | S : 'n t -> 'n s t
最后一行包含一个通用变体,或者看起来像一个变体,但没有使用关键字 of
,而是使用冒号 :
。这是为什么?我应该如何阅读这种类型?
查看类型声明的 OCaml 语法:https://ocaml.org/manual/types.html 这是否真的是变体似乎并不明显,甚至看起来这是标签和变体的混合。
这是generalized algebraic data type (GADT) 的定义。此语言功能是在 OCaml 4.0 版本中引入的,并且使用此列变体扩展了常规代数数据类型 (ADT) 的语法以启用特定于构造函数的约束。
常规 ADT 语法 <Constr> [of <args>]
用于引入具有指定参数的构造函数 <Constr>
,例如
type student = Student of string * int
通用语法 <Constr> : [<args>] -> <constr>
使用 :
而不是 of
但添加了一个额外的位置来指定类型约束,例如
type student = Student : string * int -> student
约束必须是已定义类型的实例。当定义的类型或构造函数参数(或两者)是多态的,即引用类型变量时,它很有用。一个更好的例子是表达式语言的类型安全抽象语法树,例如,
type _ exp =
| Int : int -> int exp
| Str : string -> string exp
| Cat : string exp * string exp -> string exp
| Add : int exp * int exp -> int exp
使用这种表示,我们可以编写一个静态类型的解释器,在其中我们不必处理 Add (Str "foo", Int 42)
的情况,因为由于 Cat
构造函数的约束,不可能构造这样的值,这要求两个参数都具有 string
类型。
GADT 的另一个用例是启用可用于实现动态类型和临时多态性 Haskell 类型 类 的存在类型。在存在构造函数中,一些出现在构造函数参数类型中的类型变量在约束类型中不存在,例如,
type show = Show : {data : 'a; show : 'a -> string} -> show
let show (Show {data=x;show}) = show x
所以现在我们可以有一个异构容器,
let entries = [
Show {data=42; show=string_of_int};
Show {data="foo"; show=fun x -> x};
]
我们可以展示,
# List.map show entries;;
- : string list = ["42"; "foo"]
查看以下代码:
type z = Z of z
type 'a s = Z | S of 'a
type _ t = Z : z t | S : 'n t -> 'n s t
最后一行包含一个通用变体,或者看起来像一个变体,但没有使用关键字 of
,而是使用冒号 :
。这是为什么?我应该如何阅读这种类型?
查看类型声明的 OCaml 语法:https://ocaml.org/manual/types.html 这是否真的是变体似乎并不明显,甚至看起来这是标签和变体的混合。
这是generalized algebraic data type (GADT) 的定义。此语言功能是在 OCaml 4.0 版本中引入的,并且使用此列变体扩展了常规代数数据类型 (ADT) 的语法以启用特定于构造函数的约束。
常规 ADT 语法 <Constr> [of <args>]
用于引入具有指定参数的构造函数 <Constr>
,例如
type student = Student of string * int
通用语法 <Constr> : [<args>] -> <constr>
使用 :
而不是 of
但添加了一个额外的位置来指定类型约束,例如
type student = Student : string * int -> student
约束必须是已定义类型的实例。当定义的类型或构造函数参数(或两者)是多态的,即引用类型变量时,它很有用。一个更好的例子是表达式语言的类型安全抽象语法树,例如,
type _ exp =
| Int : int -> int exp
| Str : string -> string exp
| Cat : string exp * string exp -> string exp
| Add : int exp * int exp -> int exp
使用这种表示,我们可以编写一个静态类型的解释器,在其中我们不必处理 Add (Str "foo", Int 42)
的情况,因为由于 Cat
构造函数的约束,不可能构造这样的值,这要求两个参数都具有 string
类型。
GADT 的另一个用例是启用可用于实现动态类型和临时多态性 Haskell 类型 类 的存在类型。在存在构造函数中,一些出现在构造函数参数类型中的类型变量在约束类型中不存在,例如,
type show = Show : {data : 'a; show : 'a -> string} -> show
let show (Show {data=x;show}) = show x
所以现在我们可以有一个异构容器,
let entries = [
Show {data=42; show=string_of_int};
Show {data="foo"; show=fun x -> x};
]
我们可以展示,
# List.map show entries;;
- : string list = ["42"; "foo"]