为什么 OCaml 的记录类型声明不是同义词?

kwo*_*yul 1 constructor ocaml record type-synonyms

https://cs3110.github.io/textbook/chapters/data/type_synonym.html

正如我们在上面看到的,

type a = int * int * int
Run Code Online (Sandbox Code Playgroud)

type a是 的同义词int * int * int

因此,如果我们声明int * int * int多个名称,它们都是相同的类型。

type a = int * int * int
type b = int * int * int
type c = int * int * int
Run Code Online (Sandbox Code Playgroud)

abc是相同类型。

他们没有数据构造函数。确实,不需要,也不应该,因为它们只是同义词。

然而,当我们查看记录时,情况却截然不同。

type a = { name : string }
type b = { name : string }
type c = { name : string }
Run Code Online (Sandbox Code Playgroud)

abc是不同的类型。我们可以通过下面的代码来证明。

({ name = "something" } : a) = ({ name = "something" } : b) ;;

// This expression has type b but an expression was expected of type a
Run Code Online (Sandbox Code Playgroud)

它的作用类似于 Haskell 中的“newtype”,但不使用数据构造函数。

我听说在 OCaml 中,记录是一个原始的概念,它们有自己的身份。(您可以从下面的网址看到它。)

没有记录类型的类型构造函数?

如果是的话,为什么元组和记录的行为不同?

这让我很困惑。

我了解到可以通过使用模块来解决重复记录格式的问题。所以这种混乱没什么大不了的。

但我想知道为什么元组和记录之间存在不一致。

And*_*erg 5

主要原因是类型推断。当你写一个函数时

let f x = x.a
Run Code Online (Sandbox Code Playgroud)

那么类型系统将无法判断类型f是否可以是带有字段的任何记录类型a

有一些方法可以解决这种歧义,即行多态性,这是 OCaml 用于推断对象类型的方法:

let f o = o#a  (* infers type f : < a : 'a; .. > -> 'a *)
Run Code Online (Sandbox Code Playgroud)

这里,<a : t; ..>表示任何具有a类型字段的对象类型t。然而,缺点是这种灵活性需要更重量级且成本更高的对象运行时表示。

也就是说,标准 ML(OCaml 的姊妹语言)确实使记录和元组成为同一件事(从字面上看,a * b * c只是{1 : a, 2 : b, 3 : c}SML 的简写),但随后要求程序员插入类型注释来解决可能的歧义,例如上面的例子。