SML:可泛化与不可泛化类型变量

SML: Generalizable vs. Non-Generalizable Type Variables

我刚刚在 Ch 中看到这些。 Ullman 的 ML 编程要素 中的 5 篇。他说:

"A type variable such as 'a has two different meanings. It can mean "对于每个类型 T,都有一个类型为 T 的对象实例代替 'a。这种类型变量称为可泛化。”

然后他说:

"'a也可以代表我们选择的任何一种类型。但是,选择了一种类型之后,即使我们重用使用原始类型变量描述其类型的对象,类型也不能改变'a。这种类型的变量称为不可泛化。”

有人可以举出每种类型的例子吗?很难理解这个问题,理解区别似乎很重要(他建立在这些定义的基础上)。

谢谢!

这是 SML 中 value restriction 的结果。

当语言为您的程序推断类型时,它可以找到适用于 任何 类型的表达式,它用类型变量表示。 map 函数就是一个很好的例子。 (我的语法可能有点偏差,因为我从未使用过 SML。)

fun map f nil     = nil
  | map f (x::xs) = f x :: map f xs

因为这个函数适用于 any 类型的列表,它得到一个类型变量:

('a -> 'b) -> 'a list -> 'b list

我们可以对 int liststring list 使用相同的 map 函数——这是一个 泛化类型变量 。值得注意的一件事是 nil 的情况:nil 可以很容易地成为 int 的空列表和 string 的空列表。它的类型为 'a list.

在一个完美的世界里,这就是我们所拥有的。但是有一个问题:可变引用。按照与上面相同的逻辑,以下 ref 的类型也将有一个类型变量:

val x = ref nil

我们预计 x('a list) ref。就像 nil 本身一样,它可以很容易地成为 (int list) ref(string list) ref — 或者可以吗?问题是我们可以set引用。如果我们可以使用 x 就好像 它具有更具体的类型 (int list) ref 我们可以将其设置为 [1,2,3]。然后,如果我们可以在其他地方将它用作 (string list) ref,我们可以将 [1,2,3] 读出到需要字符串列表的东西!那是个问题。

为了克服这个问题,SML 有 值限制。粗略地说,这意味着不像函数那样 "look" 的东西不能是完全多态的——它们不能有可泛化的类型变量。相反,x 的类型将基于我们使用它的第一个具体类型(即 (int list) ref)。如果我们继续并尝试将 x 与不同的具体类型一起使用,我们将收到有关不可泛化类型变量的错误。,

从某种意义上说,在我们使用x并给它一个具体类型之前,不可概括的类型变量只是一个占位符。这有点令人困惑,因为它看起来仍然像一个普通类型变量 ('a),但如果我们以不止一种方式使用它,就会给我们一个错误。我认为 OCaml 在区分两者方面做得更好。 OCaml 会将 x 的类型推断为 '_a,这在语法上与普通类型变量不同,并清楚地表明它只是一个占位符,而不是普通的多态值。

这在语言中有点瑕疵,但如果您想要这样的可变引用,这基本上是不可避免的。