Ocaml 价值限制

Ocaml 价值限制,ocaml,Ocaml,在OCaml中,不能泛化部分应用的curried函数(“值限制”) 价值限制的目的是什么?如果它不存在,会发生什么不愉快的事情?是我不久前给出的关于F#的答案;OCaml的问题与此完全相同。问题在于,如果没有它,我们将能够创建指向错误数据类型的引用: let f : 'a -> 'a option = let r = ref None in fun x -> let old = !r in r := Some x; ol

在OCaml中,不能泛化部分应用的curried函数(“值限制”)

价值限制的目的是什么?如果它不存在,会发生什么不愉快的事情?

是我不久前给出的关于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!!!

如果没有值限制或其他限制泛化的机制,该程序将被类型系统接受:

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
不是唯一一个可能要添加到介绍该问题的纯lambda演算中的构造)和对他在撰写论文(包括他的论文)时存在的系统的调查。

弱多态性
有一个很好的描述

基本上,让我们看看下面的函数(缓存它所看到的第一个值):

#让我们记住=
让cache=ref-None进入
(乐趣x->
匹配!缓存为
|一些y->y
|无->缓存:=某些x;x)
;;
val记住:''uA->''uA=
由于它涉及命令,所以应用了值限制


但是,让我们假设没有值限制

然后它的类型变成
val记住:'a->'a=


如果我现在执行
let()=记住1
1
记录在
缓存中,对吗

如果我第二次调用,
让x=3+记住2
,这应该会起作用,因为
3
是整数,
记住
返回与其参数相同的类型。我在这里给出了
2
,所以
记住
也返回整数(但值是1,因为我们已经记住了一次)。这应该通过类型检查


如果我以
的身份第三次调用y=3.0+记住2.0会怎么样?它还能工作吗

根据remember的类型和我第二次调用的原因,它也应该可以工作,因为我给了
remember
,它应该返回float

但是,由于它第一次在内部存储了
1
(整数),因此它将返回1,这是一个整数。所以类型检查会失败,对吗



我们可以看到,如果没有值限制或弱多态性,由于允许可变性,整个类型检查将有困难。在上面这种愚蠢的情况下,您需要不断手动检查或跟踪存储的初始类型
记住

@ThePiercingPrince现代OCaml中使用的解决方案令人不快的是,如果您编写纯功能性OCaml,值限制仍然适用于您,而不需要这样做(您的程序纯粹是功能性的,因此是安全的).X.L.论文中描述的一些系统没有这个缺点,但缺点是在类型中显示值的实现细节。由于模块化似乎是下一件大事,因此选择了简化模块化的解决方案。旧版本的Caml light在X.L.论文中实现了该系统……这是一个最复杂和(我很肯定)的允许键入所有纯功能的Caml程序。不过,您必须返回到非常旧的Caml light版本。正如您所说,如果要编写纯功能程序,请使用Haskell。Haskell的下一个版本无论如何都是严格的。@PiercingPrince在中查找:下一个Haskell将是严格的,下一个ML将是纯的。@ThePi王子,不要太高兴。首先,Haskell具有单态限制,这与值限制几乎相同,只是它仅适用于存在类约束的情况。其次,更糟糕的是,
unsafePerformIO
实际上引入了与值限制在ML中要解决的问题完全相同的问题。也就是说,使用
unsafePerformIO
,您可以在Haskell中编写一个不健全的泛型强制转换函数,从而使地狱大崩塌。@AndreasRossberg但作为一个正交的权衡轴,因此,可以保留模块性并消除限制的另一种选择是注释闭包的所有自由变量的类型,这显然是Scala所要求的,不是吗?
# 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>