为什么"讨论"要求很重要?

lll*_*lll 2 ocaml currying

我正在读这个OCaml走过幻灯片,我在这里找到一个有趣的问题:

在此输入图像描述

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

the type of this expression contains type variables that cannot be generalized
Run Code Online (Sandbox Code Playgroud)

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

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

谁能给我一些帮助?

Jef*_*eld 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
Run Code Online (Sandbox Code Playgroud)

问题仍存在于顶层; 如果你试图用来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
# 
Run Code Online (Sandbox Code Playgroud)

请注意,如果您只使用oops一种类型的列表(不是零或两个),则不会出现错误.如果您使用oops(导出的符号)零次,编译器可以抱怨,因为它可以看到整个模块并知道如何oops使用.toplevel不能抱怨零使用,因为它永远不会知道你接下来可能会输入什么.

更新

对不起,我应该多说一下实际的错误是什么(在我理解的层面).在我看来,这与currying没什么关系.

这是臭名昭着的"价值限制".值限制的简短描述如下:有些表达式无法安全地推广.也就是说,你不能使它们具有多态性,从而允许它们与所有类型一起使用.最简单的例子是这样的:

let mylist = ref []
Run Code Online (Sandbox Code Playgroud)

如果您允许mylist使用该类型'a list ref,则可以在其中存储所有不同类型的列表,这是不安全的.

但是,可以安全地概括:

let mylist2 = []
Run Code Online (Sandbox Code Playgroud)

概括mylist2到类型没有问题'a list.

在像OCaml这样的现代ML衍生品中,对泛化的限制已经简化为或多或少的简单规则.[]可以推广"值"(类似).不是值(如ref [])的表达式不能一概而论.

表达方式:

fold_left (fun a _ -> a + 1) 0
Run Code Online (Sandbox Code Playgroud)

不是一个价值.它是一个功能应用程序,具有相同的粗略形式ref [].这(显然)是oops上面的定义.

另一方面,表达式:

fun xs -> fold_left (fun a _ -> a + 1) 0 xs
Run Code Online (Sandbox Code Playgroud)

一个价值; 这是一个lambda.所以它可以推广.这(在展开方便的句法缩写之后)是len上面的定义.这就是为什么len适用于所有列表,但oops不是那么有用.

值与非值之间的区别是句法; 也就是说,只需通过本地查看表达式的形式即可确定.您无需了解表达式的类型或含义即可进行确定.

在当前形式中使用值限制的一个论点是,在大多数情况下,您可以通过以形式len而不是以形式定义函数来恢复函数的所需泛化(即,多态性)oops.这种简单的转换称为"eta扩展".

eta扩展仅改变语法但不改变函数含义的事实表明值限制只是一种近似.也就是说,它禁止对一些表达式进行泛化,这样可以安全地推广.但它很好,很简单,并且在真实程序中出现的情况下并没有太多限制.

自OCaml 3.07起,OCaml的价值限制已从基本ML改进,因为它允许在更多情况下进行推广.你可以在这里阅读:J.Garrigue,放宽价值限制.本文还包含价值限制及其历史的优秀胶囊摘要.