Types 将其参数应用于自身的函数?

Types 将其参数应用于自身的函数?,types,functional-programming,sml,smlnj,Types,Functional Programming,Sml,Smlnj,考虑以下SML函数: fn x => x x (define (apply-to-self x) (x x)) 这会产生以下错误(新泽西州标准ML v110.72): 我可以理解为什么这是不允许的——首先,我不确定如何写下它的类型——但这并不是完全荒谬的;例如,我可以将identity函数传递给它并将其取回 这个函数有名字吗?(有没有办法用SML来表达它?没有办法用类似ML类型系统的语言来表达这个函数。即使使用identity函数,它也不会工作,因为对于某些类型的中的第一个x和第二个x

考虑以下SML函数:

fn x => x x
(define (apply-to-self x) (x x))
这会产生以下错误(新泽西州标准ML v110.72):

我可以理解为什么这是不允许的——首先,我不确定如何写下它的类型——但这并不是完全荒谬的;例如,我可以将identity函数传递给它并将其取回


这个函数有名字吗?(有没有办法用SML来表达它?

没有办法用类似ML类型系统的语言来表达这个函数。即使使用identity函数,它也不会工作,因为对于某些类型的
中的第一个
x
和第二个
x
必须是该函数的不同实例,类型分别为
(\u a->\u a)->(\u a->\u a)

事实上,类型系统被设计成禁止像这样的构造

(λx . x x) (λx . x x)
在非类型lambda演算中。在动态类型语言方案中,您可以编写以下函数:

fn x => x x
(define (apply-to-self x) (x x))
并取得了预期的效果

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

像这样的函数在定点组合器中经常遇到。e、 g.其中一种形式是
λf.(λx.f(x x))(λx.f(x x))
。不动点组合器用于实现非类型lambda演算中的一般递归,而无需任何额外的递归构造,这是使非类型lambda演算图灵完整的部分原因

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

像标准ML这样的真正静态类型编程语言需要内置的递归机制来解决这个问题,例如命名递归函数(用
val rec
fun
定义)和命名递归数据类型

仍然可以使用递归数据类型来模拟您想要的东西,但它没有那么漂亮

基本上,您需要定义一个类型,如
'a foo='a foo->'a
;但是,这是不允许的。而是将其包装在一个数据类型中:
datatype'a foo=wrap of('a foo->'a)

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

每当您需要对自身调用函数时,而不是
x
,您必须显式地编写
(展开x)x
(或
展开x
);i、 e.
unwrap
将其转换为一个函数,然后应用于原始值


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

如果有人感兴趣,我发现它被称为,但找不到更多关于它的信息。我不知道它被称为U combinator,但正如该页所述,它用于构建(显然是最短的)我在回答中提到的非类型lambda演算的非终止程序。“因为x中的第一个x和第二个x必须是该函数的不同实例”:出于好奇,对于具有延迟求值的语言呢?除此之外,我不确定SML中是否有类似“实例”的东西(除了有副作用的地方)。这个问题只是一个类型问题(没有类型,通常没有意义,但并不总是)。@Hibou57的实例,我指的是泛型函数的不同具体类型实例化。“我不知道有任何标准ML实现具有类似的递归类型功能”:SML的定义不允许它,因此,任何实现都不会有这种情况。需要将其包装为数据类型可能是有道理的。看一看像
'af='af->'a
这样的类型定义,我觉得它是一个重写规则或一个计算,而不是一个类型。但可能我对此有偏见。