在OCaml中,你不能泛化部分应用的柯里化函数(“值约束”)。
值约束的目的是什么?如果它不存在会出现什么不好的情况?
值约束的目的是什么?如果它不存在会出现什么不好的情况?
如果没有价值限制或其他限制泛化的机制,这个程序将被类型系统接受:
let r = (fun x -> ref x) [];; (* this is the line where the value restriction would trigger *)
> r : 'a list ref
r := [ 1 ];;
let cond = (!r = [ "foo" ]);;
变量r
的类型应为'a list ref
,这意味着它的内容可以与[ "foo" ]
进行比较,即使它包含一个整数列表。ref
并不是唯一可能添加到纯λ演算中引入问题的构造),以及他的论文所提到的系统的概述(包括他的系统)。这里是我之前关于F#的答案;用OCaml时也会遇到同样的问题。问题在于,如果没有它,我们就可能创建指向错误类型数据的引用:
let f : 'a -> 'a option =
let r = ref None in
fun x ->
let old = !r in
r := Some x;
old
f 3 // r := Some 3; returns None : int option
f "t" // r := Some "t"; returns Some 3 : string option!!!
这里有一个对于弱多态性(weakly polymorphism)的很好描述,可以在此处查看(side-effects-and-weak-polymorphism)。
基本上,让我们看一下下面这个函数(它会缓存它所见到的第一个值):
# let remember =
let cache = ref None in
(fun x ->
match !cache with
| Some y -> y
| None -> cache := Some x; x)
;;
val remember : '_a -> '_a = <fun>
val remember : 'a -> 'a = <fun>
。
let () = remember 1
,那么1
将被记录在cache
中,对吗?let x = 3 + remember 2
,这应该可以工作,因为3
是整数,remember
返回与其参数相同的类型。我在这里给出2
,因此remember
也会返回整数(但值为1,因为我们已经记住了一次)。这应该通过类型检查。
let y = 3.0 + remember 2.0
,会再次工作吗?remember
提供了浮点数,它应该返回浮点数。1
,所以它将返回一个整数1
。因此,类型检查将失败,对吗?
remember
存储的初始类型。
unsafePerformIO
实际上引入了ML中值限制旨在解决的问题。也就是说,使用unsafePerformIO
,你可以在Haskell中编写一个不安全的通用转换函数,并让一切都失控。 - Andreas Rossberg