A function that applies its argument to itself?

Consider the following SML function:

fn x => xx 

This results in the following error (New Jersey Standard ML v110.72):

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

I can understand why this is not allowed - firstly, I’m not quite sure how to write down what its type will be, but it’s not completely meaningless; for example, I could give him an identification function and return it.

Is there a name for this function? (Is there a way to express this in SML?)

+7
source share
2 answers

It is not possible to express this function in a language with a system such as ML. It will not work even with the identification function, because the first x and the second in xx must be different instances of this function of the type (_a -> _a) -> (_a -> _a) and _a -> _a , respectively, for some type _a .

Actually, type systems are designed to prohibit constructions like

 (λx . xx) (λx . xx) 

in untyped lambda calculus. On a dynamically typed language scheme, you can write this function:

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

and get the expected result

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

Such functions are often found in fixed-point combinators. for example, one form of Y combinator is written λf.(λx.f (xx)) (λx.f (xx)) . Fixed-point combinators are used to implement general recursion in an untyped lambda calculus without any additional recursive constructs, and this is part of what the untyped Turing-complete lambda calculus does.

When people developed a simply typed lambda calculus , which is a naive static type system at the top of the lambda calculus, they found that it was not possible to write such functions. In fact, it is not possible to perform general recursion in a simply typed lambda calculus; and therefore, a simple typed lambda calculus is no longer Turing. (One interesting side effect is that programs in simply typed lambda calculus always end.)

In real statically typed programming languages ​​such as Standard ML, built-in recursive mechanisms are needed to solve the problem, such as named recursive functions (defined using val rec or fun ) and called recursive data types.

You can still use recursive data types to mimic something like what you want, but it's not so pretty.

Basically, you want to define a type of type 'a foo = 'a foo -> 'a ; however, this is not permitted. Instead, you transfer it as data: datatype 'a foo = Wrap of ('a foo -> 'a);

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

Basically, Wrap and unwrap are used to convert between 'a foo and 'a foo -> 'a and vice versa.

Whenever you need to call a function yourself, instead of xx , you must explicitly write (unwrap x) x (or unwrap xx ); those. unwrap turns it into a function that can then be applied to the original value.

PS Another ML language, OCaml, has the ability to enable recursive types (usually disabled); if you run an interpreter or compiler with the -rectypes flag, then you can write things like fun x -> xx . Basically, behind the scenes, the type controller determines where you need to “wrap” and “expand” the recursive type, and then insert them for you. I do not know of any standard implementation of ML that has similar functions of recursive types.

+4
source

All Articles