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 list
和 string 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
,这在语法上与普通类型变量不同,并清楚地表明它只是一个占位符,而不是普通的多态值。
这在语言中有点瑕疵,但如果您想要这样的可变引用,这基本上是不可避免的。
我刚刚在 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 list
和 string 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
,这在语法上与普通类型变量不同,并清楚地表明它只是一个占位符,而不是普通的多态值。
这在语言中有点瑕疵,但如果您想要这样的可变引用,这基本上是不可避免的。