为什么" currying"要求很重要?

时间:2014-04-20 03:54:27

标签: ocaml currying

我正在阅读这些OCaml walking through幻灯片,我在这里找到一个有趣的问题:

enter image description here

似乎oops函数会产生编译错误:

the type of this expression contains type variables that cannot be generalized

我不知道原因所以我在Mac上使用OCaml版本4.01.0快速对此功能进行一些测试,令我惊讶的是,我在解释此代码时没有看到任何错误

我不知道为什么幻灯片因为curry声称它是一个错误,我无法重新创建此错误...

有人能给我一些帮助吗?

1 个答案:

答案 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。本文还包含价值限制及其历史的优秀胶囊摘要。