I am really new to scheme functional programming. I recently came across Y-combinator function in lambda calculus, something like this Y ≡ (λy.(λx.y(xx))(λx.y(xx)))
. I wanted to implement it in scheme, i searched alot but i didn't find any implementation which exactly matches the above given structure. Some of them i found are given below:
(define Y
(lambda (X)
((lambda (procedure)
(X (lambda (arg) ((procedure procedure) arg))))
(lambda (procedure)
(X (lambda (arg) ((procedure procedure) arg)))))))
and
(define Y
(lambda (r)
((lambda (f) (f f))
(lambda (y)
(r (lambda (x) ((y y) x)))))))
As you can see, they dont match with the structure of this Y ≡ (λy.(λx.y(xx))(λx.y(xx)))
combinator function. How can I implement it in scheme in exactly same way?
In a lazy language like Lazy Racket you can use the normal order version, but not in any of the applicative order programming languages like Scheme. They will just go into an infinite loop.
The applicative version of Y is often called a Z combinator:
Now the first thing that happens when this is applied is
(g g)
and since you can always substitute a whole application with the expansion of it's body the body of the function can get rewritten to:I haven't really changed anything. It's just a little more code that does exactly the same. Notice this version uses
apply
to support multiple argument functions. Imagine the Ackermann function:This can be done with
Z
like this:Notice the implementations is exactly the same and the difference is how the reference to itself is handled.
EDIT
So you are asking how the evaluation gets delayed. Well the normal order version looks like this:
If you look at how this would be applied with an argument you'll notice that Y never returns since before it can apply
f
in(f (g g))
it needs to evaluate(g g)
which in turn evaluates(f (g g))
etc. To salvage that we don't apply(g g)
right away. We know(g g)
becomes a function so we just givef
a function that when applied will generate the actual function and apply it. If you have a functionadd1
you can make a wrapper(lambda (x) (add1 x))
that you can use instead and it will work. In the same manner(lambda args (apply (g g) args))
is the same as(g g)
and you can see that by just applying substitution rules. The clue here is that this effectively stops the computation at each step until it's actually put into use.