将其论证应用于自身的函数?

时间:2012-02-06 15:29:03

标签: types functional-programming sml smlnj

考虑以下SML功能:

fn x => x x

这会产生以下错误(新泽西标准ML v110.72):

stdIn:1.9-1.12 Error: operator is not a function [circularity]
  operator: 'Z
  in expression:
    x x

我可以理解为什么不允许这样做 - 一方面,我不确定如何写下它的类型 - 但它并非完全没有意义;例如,我可以将身份功能传递给它并将其取回。

这个功能有名字吗? (有没有办法在SML中表达它?)

2 个答案:

答案 0 :(得分:7)

无法用具有ML类型系统的语言表达此功能。即使使用身份功能,它也行不通,因为x中的第一个x x和第二(_a -> _a) -> (_a -> _a)必须是该功能的不同实例,类型为_a -> _a和{{1} },分别用于某种类型_a

事实上,类型系统被设计为禁止像

这样的结构
(λx . x x) (λx . x x)

在无类型的lambda演算中。在动态类型语言Scheme中,您可以编写此函数:

(define (apply-to-self x) (x x))

并获得预期结果

> (define (id x) x)
> (eq? (apply-to-self id) id)
#t

答案 1 :(得分:4)

这样的函数经常在定点组合器中遇到。例如Y combinator的一种形式是λf.(λx.f (x x)) (λx.f (x x))。定点组合器用于在无类型的lambda演算中实现一般递归,而不需要任何额外的递归构造,这是使无类型lambda演算Turing-complete的部分原因。

当人们开发simply-typed lambda calculus,这是一个基于lambda演算的天真静态类型系统时,他们发现不再可能编写这样的函数。实际上,在简单类型的lambda演算中不可能执行一般递归;因此,简单类型的lambda演算不再是图灵完备的。 (一个有趣的副作用是简单类型的lambda演算中的程序总是终止。)

标准ML等真正的静态类型编程语言需要内置的递归机制来克服这个问题,例如命名递归函数(用val recfun定义)和命名递归数据类型。

仍然可以使用递归数据类型来模拟你想要的东西,但它不是那么漂亮。

基本上,您想要定义类似'a foo = 'a foo -> 'a的类型;但是,这是不允许的。您可以将其包装在数据类型中:datatype 'a foo = Wrap of ('a foo -> 'a);

datatype 'a foo = Wrap of ('a foo -> 'a);
fun unwrap (Wrap x) = x;

基本上,Wrapunwrap用于在'a foo'a foo -> 'a之间进行转换,反之亦然。

每当你需要自己调用一个函数而不是x x时,你必须明确地写(unwrap x) x(或unwrap x x);即unwrap将其转换为可以应用于原始值的函数。

P.S。另一种ML语言OCaml可以选择启用递归类型(通常是禁用的);如果您使用-rectypes标志运行解释器或编译器,则可以编写fun x -> x x之类的内容。基本上,在幕后,类型检查器会找出需要“包装”和“解包”递归类型的位置,然后为您插入它们。我不知道任何具有类似递归类型功能的标准ML实现。