为什么`id id`不是OCaml中的值?

时间:2017-02-10 00:33:38

标签: lambda functional-programming ocaml lambda-calculus value-restriction

我仍在尝试理解OCaml中的价值限制,而我正在阅读Wright's paper。在它中,状态(fun x -> x) (fun y -> y)不是语法值,而它还表明lambda表达式应该是一个值。我在这里有点困惑,本质上它不是id id也是lambda表达式吗?什么真的算作OCaml中的句法价值?

我也在utop中尝试过并找到了这些:

utop # let x = let x = (fun y -> y) (fun z -> z)  in x ;;
val x : '_a -> '_a = <fun>

此处id id不是值,它无法转义值限制,但

utop # let x a  = let x = (fun y -> y) a in x ;;
val x : 'a -> 'a = <fun>

此处id a似乎被视为一个值。

它们都是功能应用,有什么区别?

2 个答案:

答案 0 :(得分:3)

因此,这里涉及两个概念:let-polymoprhism和value restriction。 Let-polymorphism不允许对所有非let-bound的值进行类型泛化。或者,在不使用双重否定的情况下,只有在使用let-binding引入值时,它才允许值为多态。这是一种过度近似,即它可能不允许有效程序(存在误报),但它永远不会允许无效程序(它将保持健全性)。

值限制是另一种过度近似,这是保持命令式程序健全性所必需的。它不允许非语法值的多态性。 OCaml使用这种过度近似的更精确版本,称为relaxed value restriction(实际上允许某些非语法值是多态的)。

但我首先要解释什么是句法价值:

非正式地,语法值是一个可以在不进行任何计算的情况下进行求值的表达式,例如,考虑以下let绑定:

let f = g x 

此处f不是语法值,因为为了获得计算表达式g x所需的值。但是,在下面,

let f x = g x

f是句法的,如果我们将糖除去,那就更明显了:

let f = fun x -> g x

现在显而易见的是,f是语法的,因为它绑定到lambda表达式。

该值称为 syntactic ,因为它直接在程序中定义(在语法中)。基本上,它是一个可以在静态时间计算的常量值。稍微更正式的是,以下值被认为是语法:

  • 常量(即整数和浮点文字之类的东西)
  • 仅包含其他简单值的构造函数
  • 函数声明,即以fun或function开头的表达式,或等效的let绑定,let f x = ...
  • 让表达式在expr2中使用var = expr1,其中expr1和expr2都是简单值

现在,当我们非常确定什么是语法时,我们会更仔细地看看你的例子。让我们从Wright的例子开始,实际上:

let f = (fun x => x) (fun y => y)

或通过介绍let id = fun x -> x

let f = id id

您可能会看到f这里不是语法值,尽管id是句法。但是为了得到f的值,你需要计算 - 所以这个值是在运行时定义的,而不是在编译时定义的。

现在,让我们来看看你的例子:

let x a = let x = (fun y -> y) a in x
==>
let x = fun a -> let x = (fun y -> y) a in x 

我们可以看到,x是一个句法值,因为左边是一个lambda表达式。 lambda表达式的类型是'a -> 'a。您可能会问,为什么表达式的类型不是'_a -> '_a。这是因为值限制仅在顶层引入,而lambda表达式还不是值,它是一个表达式。通俗地说,首先,最普遍的Hindley-Milner类型是在假设下推断出的,没有副作用,然后推断类型被(宽松)值限制削弱。类型推断的范围是let绑定。

这是所有的理论,有时为什么有些表达式是良好类型并不是很明显,而具有相同语义但写得略有不同的表达式并不是很好的类型。直觉可能会说,这里有些不对劲。是的,事实上,let f = id id是一个格式良好的程序,被类型检查器拒绝,这是过度逼近的一个例子。如果我们将这个程序转换为let f x = id id x它突然变成一个具有通用类型的良好类型的程序,虽然转换不会改变语义(并且两个程序实际上都编译为相同的机器代码)。这是类型系统的限制,它是简单性和精确性之间的折衷(稳健性不能成为妥协的一部分 - 类型检测器必须是健全的)。因此,从理论上看,为什么后一个例子总是安全的,这一点完全不明显。只是为了实验,让我们试着玩你的例子,并尝试打破程序:

# let x = fun a -> let z = ref None in let x = (fun y -> z := Some y; y) a in x ;;
val x : 'a -> 'a = <fun>

所以,我们在这里添加了一个引用z,并且我们试图存储该值,以便在不同的应用程序下对不同的类型,我们应该能够存储到不同类型的相同引用值。但是,这是完全不可能的,因为因为x是一个语法值,所以保证每个类型x k都被称为新引用,并且这个引用永远不会泄露让画质。希望,这有助于:)

答案 1 :(得分:2)

这是一个应用程序,而不是lambda表达式。左表达式是一个函数,右表达式是应用函数的值。

值的概念(在价值限制的意义上)是句法概念。它与价值的类型无关。

相关问题