我正在使用OCaml来实现Chris Okasaki的Purely Functional Data Structures中的一些数据结构,并且遇到了这种类型的定义:
type tree = Node of int * int * tree list;;
我认为它不需要标签,因为它不是联合类型,所以我尝试删除标签,但是我收到了以下错误:
# type tree = int * int * tree list;;
Characters 5-33:
type tree = int * int * tree list;;
^^^^^^^^^^^^^^^^^^^^^^^^^^^^
Error: The type abbreviation tree is cyclic
为什么两个看似相同的类型定义会发生这种情况?
答案 0 :(得分:8)
在类似ML的语言中,递归类型的定义是递归不通过变体类型的定义。这是一个实用的定义,因为它往往会导致更有用的类型检查。
关于递归类型没有什么难以处理的。您可以使用-rectypes
标志在OCaml中启用对递归类型的支持。
$ ocaml -rectypes
OCaml version 4.02.1
# type tree = int * int * tree list;;
type tree = int * int * tree list
# let x: tree = (3, 3, [(4, 4, [])]);;
val x : tree = (3, 3, [(4, 4, [])])
启用递归类型时,会出现所有常见的强类型保证。主要缺点是许多非预期的程序被接受。换句话说,递归类型的存在通常表示编程错误。
答案 1 :(得分:1)
第一个类型定义定义了新类型。省略构造函数名称时,不是定义新类型,而是实际引入类型缩写。默认情况下,类型缩写不允许递归,因为通常这不是你的意思。
您可以使用定义新类型的任何类型定义语法来创建递归类型,而不仅仅是变体。记录也可以,例如
type tree = { node : int * int * tree list }
甚至更好
type tree = {
value : int;
depth : int;
children : tree list;
}
(注意:字段名称是任意选择的,因为我不知道它们的最初目的)
总而言之,sum类型不仅用于引入不相交的类型集,还用于创建新类型,因此:
type t = Constr x
引入了类型t
,可以使用类型为Constr
的值的构造函数x
构建。