我正在阅读这些OCaml walking through幻灯片,我在这里找到一个有趣的问题:
似乎oops函数会产生编译错误:
the type of this expression contains type variables that cannot be generalized
我不知道原因所以我在Mac上使用OCaml版本4.01.0快速对此功能进行一些测试,令我惊讶的是,我在解释此代码时没有看到任何错误
我不知道为什么幻灯片因为curry声称它是一个错误,我无法重新创建此错误...
有人能给我一些帮助吗?
答案 0 :(得分:9)
编译器抱怨此错误,但顶层没有。
$ cat ungen.ml
open List
let oops = fold_left (fun a _ -> a + 1) 0
$ ocamlc -c ungen.ml
File "ungen.ml", line 2, characters 11-41:
Error: The type of this expression, '_a list -> int,
contains type variables that cannot be generalized
问题仍存在于顶层;如果您尝试使用oops
计算两个不同类型列表的长度,您会看到它:
$ ocaml
OCaml version 4.01.0
# open List;;
# let oops = fold_left (fun a _ -> a + 1) 0;;
val oops : '_a list -> int = <fun>
# oops [1;2;3;4];;
- : int = 4
# oops ['a';'b';'c'];;
Error: This expression has type char but an expression was expected of type
int
#
请注意,如果您使用oops
只有一种类型的列表(不是零或两个),则没有错误。如果您使用oops
(导出的符号)零次,编译器会抱怨,因为它可以看到整个模块并知道如何使用oops
。 toplevel不能抱怨零使用,因为它永远不知道你接下来可能输入什么。
<强>更新强>
对不起,我应该多说一下实际的错误(在我理解的层面)。在我看来,这与currying没什么关系。
这是臭名昭着的“价值限制”。值限制的简短描述如下:有些表达式无法安全地推广。也就是说,你不能使它们具有多态性,因此允许它们与所有类型一起使用。最简单的例子是这样的:
let mylist = ref []
如果您允许mylist
拥有'a list ref
类型,则可以在其中存储所有不同类型的列表,这是不安全的。
但是,可以安全地概括:
let mylist2 = []
将mylist2
概括为类型'a list
没有问题。
在像OCaml这样的现代ML衍生品中,对泛化的限制已经简化为一个或多或少的简单规则。可以推广“值”(如[]
)。
ref []
)。
表达式:
fold_left (fun a _ -> a + 1) 0
不是值。它是一个函数应用程序,具有与ref []
相同的粗略形式。这(显然)是上面oops
的定义。
另一方面,表达式:
fun xs -> fold_left (fun a _ -> a + 1) 0 xs
IS 一个值;这是一个lambda。所以它可以推广。这(在展开方便的句法缩写之后)是上面len
的定义。这就是len
适用于所有列表的原因,但oops
并不是那么有用。
值和非值之间的区别是句法;即,可以仅通过本地查看表达的形式来确定。您无需了解表达式的类型或含义即可进行确定。
在当前形式中使用值限制的一个论点是,在大多数情况下,您可以通过以len
而不是在oops
的形式定义函数来恢复函数的所需泛化(即多态性)。 {{1}}的形式。这种简单的转换称为“eta扩展”。
eta扩展仅改变语法但不改变函数含义的事实表明值限制只是一种近似。也就是说,它禁止对一些表达式进行泛化,这样可以安全地推广。但它很好,很简单,对于真实程序中出现的情况也没有太多限制。
自OCaml 3.07起,OCaml的价值限制已从基本ML改进,因为它允许在更多情况下进行推广。你可以在这里阅读:J. Garrigue, Relaxing the Value Restriction。本文还包含价值限制及其历史的优秀胶囊摘要。