为什么我的类型定义在声明为变体时被拒绝为循环,但在其他情况下被接受?

Why is my type definition rejected as cyclic when declared as a variant, but accepted otherwise?

我在使用 OCaml 实现 Chris Okasaki 的纯函数式数据结构中的一些数据结构时遇到了这个类型定义:

 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

为什么两个看似相同的类型定义会发生这种情况?

在类 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, [])])

启用递归类型时,所有常见的强类型保证都存在。主要的缺点是接受了许多非预期的程序。换句话说,递归类型的存在通常表明存在编程错误。

第一个类型定义定义了新类型。当您省略构造函数名称时,您实际上不是在定义新类型,而是在引入类型缩写。默认情况下,类型缩写不允许递归,因为通常这不是你的意思。

您可以使用任何定义新类型的类型定义语法来创建递归类型,而不仅仅是变体。记录也可以,例如

type tree = { node : int * int * tree list }

甚至更好

type tree = {
  value : int;
  depth : int;
  children : tree list;
}

(注意:字段名称是任意选择的,因为我不知道它们的原始用途)

总而言之,求和类型不仅用于引入不相交的类型集,还用于创建新类型,因此:

 type t = Constr x

引入类型 t,可以使用构造函数 Constr 从类型 x.

的值构造