Y Combinator implementation Scheme - scheme

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:
(define Z
(lambda (f)
((lambda (g) (g g))
(lambda (g)
(f (lambda args (apply (g g) args)))))))
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:
(define Z
(lambda (f)
((lambda (g)
(f (lambda args (apply (g g) args))))
(lambda (g)
(f (lambda args (apply (g g) args)))))))
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:
(define ackermann
(lambda (m n)
(cond
((= m 0) (+ n 1))
((= n 0) (ackermann (- m 1) 1))
(else (ackermann (- m 1) (ackermann m (- n 1)))))))
(ackermann 3 6) ; ==> 509
This can be done with Z like this:
((Z (lambda (ackermann)
(lambda (m n)
(cond
((= m 0) (+ n 1))
((= n 0) (ackermann (- m 1) 1))
(else (ackermann (- m 1) (ackermann m (- n 1))))))))
3
6) ; ==> 509
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:
(define Y
(lambda (f)
((lambda (g) (g g))
(lambda (g) (f (g g))))))
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 give f a function that when applied will generate the actual function and apply it. If you have a function add1 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.

Related

temporarily overwrite a globally defined function in Scheme let block?

suppose I have the following functions:
(define (g x) (f x))
(define (f x) (+ 1 x))
I would like to temporarily call g with a different f. For example, something like this:
(let ((f (lambda (x) (+ 2 x))))
(g 5))
I would like the code above to evaluate to 7, but it doesn't. Instead, it evaluates to 6, since g calls the f outside the scope of the let.
Is there a way to do this without redefining g inside the let, and without inlining the entire body of the definition of g in the let? (In practice, g may be a very large, complicated function).
What you are asking for is dynamic rather than lexical binding of 'f'. R6RS and R7RS support this with parameters. This will do what you want:
(define f (make-parameter (lambda (x) (+ 1 x))))
(define (g x) ((f) x))
(display (g 5))(newline)
(parameterize ((f (lambda (x) (+ 2 x))))
(display (g 5))(newline))
I'm not sure that you can, but I'm by no means a Scheme expert.
I realise that you're trying to achieve this without redefining g inside the let, but how about:
(define (h f x) (f x))
(define (g x) (h f x))
(define (f x) (+ 1 x))
(let ((f (lambda (x) (+ 2 x))))
(h f 5))
That way, you preserve the behaviour of g where it's currently being called. But where you want to temporarily have a different behaviour, you can call h instead.
A bit more code for clarification:
(let ((f (lambda (x) (+ 2 x))))
(display (g 5)) ; 6
(newline)
(h f 5)) ; 7
You could use an optional parameter in g to pass the f from the let expression.
(define (g x . args)
(if (null? args)
(f x)
((car args) x)))
and
(let ((f (lambda (x) (+ 2 x))))
(g 5 f))
I found a way to do exactly what I wanted, although I have a feeling many people will not consider this kosher:
(define (g x) (f x))
(define (f x) (+ 1 x))
(let ((old-f f))
(set! f (lambda (x) (+ 2 x)))
(let ((ans (g 5)))
(set! f old-f)
ans))
; -> 7
(g 5) ; -> 6
edit In response to the comment below, I wasn't even aware that fluid-let was a thing. It even already works on MIT-Scheme. That's actually exactly what I needed. If commenter below posts something like this as an answer, it will be made the accepted answer:
(define (g x) (f x))
(define (f x) (+ 1 x))
(fluid-let ((f (lambda (x) (+ x 2))))
(g 5)) ; -> 7
(g 5) ; -> 6

Understanding extra arguments in the Y Combinator in Scheme

According to RosettaCode, the Y Combinator in Scheme is implemented as
(define Y
(λ (h)
((λ (x) (x x))
(λ (g)
(h (λ args (apply (g g) args)))))))
Of course, the traditional Y Combinator is λf.(λx. f(x x))(λx. f(x x))
My question, then, is about h and args, which don't appear in the mathematical definition, and about apply, which seems like it should either be in both halves of the Combinator or in neither.
Can someone help me understand what is going on here?
Lets start off with the lambda calculus version traslated to Scheme:
(λ (f)
((λ (x) (f (x x)))
(λ (x) (f (x x)))))
I'd like to simplify this since I see (λ (x) (f x x)) is repeated twice. You can substitute the beginning there to this:
(λ (f)
((λ (b) (b b))
(λ (x) (f (x x)))))
Scheme is an eager language so it will go into an infinite loop. In order to avoid that we make a proxy.. Imagine you have + that takes two numbers, you can substitute it with (λ (a b) (+ a b)) without the result being changed. Lets do that with the code:
(λ (f)
((λ (b) (b b))
(λ (x) (f (λ (p) ((x x) p))))))
Actully this has its own name. It's called the Z combinator. (x x) is not done when f is applied only when the supplied proxy is applied. Delayed one step. It might look strange but I know (x x) becomes a function so this is exactly the same as my + substitution above.
In Lambda calculus all functions takes one argument. If you see f x y it's actually the same as ((f x) y) in Scheme. If you want it to work with functions of all arities your substitution needs to reflect that. In Scheme we have rest arguments and apply to do this.
(λ (f)
((λ (b) (b b))
(λ (x) (f (λ p (apply (x x) p))))))
This isn't neede if you only are going to use one arity functions as in lambda calculus.
Notice that in your code you use h instead of f. It doesn't really matter what you call the variables. This is the same code with different names. So this is the same:
(λ (rec-fun)
((λ (yfun) (yfun yfun))
(λ (self) (rec-fun (λ args (apply (self self) args))))))
Needless to say (yfun yfun) and (self self) does the same thing.

Why Scheme requires apply in Y-combinator implementation, but Racket doesn't?

Here is the Y-combinator in Racket:
#lang lazy
(define Y (λ(f)((λ(x)(f (x x)))(λ(x)(f (x x))))))
(define Fact
(Y (λ(fact) (λ(n) (if (zero? n) 1 (* n (fact (- n 1))))))))
(define Fib
(Y (λ(fib) (λ(n) (if (<= n 1) n (+ (fib (- n 1)) (fib (- n 2))))))))
Here is the Y-combinator in Scheme:
(define Y
(lambda (f)
((lambda (x) (x x))
(lambda (g)
(f (lambda args (apply (g g) args)))))))
(define fac
(Y
(lambda (f)
(lambda (x)
(if (< x 2)
1
(* x (f (- x 1))))))))
(define fib
(Y
(lambda (f)
(lambda (x)
(if (< x 2)
x
(+ (f (- x 1)) (f (- x 2))))))))
(display (fac 6))
(newline)
(display (fib 6))
(newline)
My question is: Why does Scheme require the apply function but Racket does not?
Racket is very close to plain Scheme for most purposes, and for this example, they're the same. But the real difference between the two versions is the need for a delaying wrapper which is needed in a strict language (Scheme and Racket), but not in a lazy one (Lazy Racket, a different language).
That wrapper is put around the (x x) or (g g) -- what we know about this thing is that evaluating it will get you into an infinite loop, and we also know that it's going to be the resulting (recursive) function. Because it's a function, we can delay its evaluation with a lambda: instead of (x x) use (lambda (a) ((x x) a)). This works fine, but it has another assumption -- that the wrapped function takes a single argument. We could just as well wrap it with a function of two arguments: (lambda (a b) ((x x) a b)) but that won't work in other cases too. The solution is to use a rest argument (args) and use apply, therefore making the wrapper accept any number of arguments and pass them along to the recursive function. Strictly speaking, it's not required always, it's "only" required if you want to be able to produce recursive functions of any arity.
On the other hand, you have the Lazy Racket code, which is, as I said above, a different language -- one with call-by-need semantics. Since this language is lazy, there is no need to wrap the infinitely-looping (x x) expression, it's used as-is. And since no wrapper is required, there is no need to deal with the number of arguments, therefore no need for apply. In fact, the lazy version doesn't even need the assumption that you're generating a function value -- it can generate any value. For example, this:
(Y (lambda (ones) (cons 1 ones)))
works fine and returns an infinite list of 1s. To see this, try
(!! (take 20 (Y (lambda (ones) (cons 1 ones)))))
(Note that the !! is needed to "force" the resulting value recursively, since Lazy Racket doesn't evaluate recursively by default. Also, note the use of take -- without it, Racket will try to create that infinite list, which will not get anywhere.)
Scheme does not require apply function. you use apply to accept more than one argument.
in the factorial case, here is my implementation which does not require apply
;;2013/11/29
(define (Fact-maker f)
(lambda (n)
(cond ((= n 0) 1)
(else (* n (f (- n 1)))))))
(define (fib-maker f)
(lambda (n)
(cond ((or (= n 0) (= n 1)) 1)
(else
(+ (f (- n 1))
(f (- n 2)))))))
(define (Y F)
((lambda (procedure)
(F (lambda (x) ((procedure procedure) x))))
(lambda (procedure)
(F (lambda (x) ((procedure procedure) x))))))

How do I make the substitution ? Scheme

How do I make the substitution? I tried to trace but I don't really get what is going on...
the code:
(define (repeated f n)
(if (zero? n)
identity
(lambda (x) ((repeated f (- n 1)) (f x)))))
f is a function and n is an integer that gives the number of times we should apply f.
....can someone help me to interpret it. I know it returns several procedures and i want to believe that it goes f(f(f(x)))
okey i will re-ask this question but in different manner, because i didn't really get an answer last time. consider this code
(define (repeated f n)
(if (zero? n)
identity
(lambda (x) ((repeated f (- n 1)) (f x)))))
where n is a positive integer and f is an arbitrary function: how does scheme operate on this code lets say we give (repeated f 2). what will happen? this is what think:
(f 2)
(lambda (x) ((repeated f (- 2 1)) (f x))))
(f 1)
(lambda (x) ((lambda (x) ((repeated f (- 1 1)) (f x)))) (f x))))
(f 0)
(lambda (x) ((lambda (x) (identity (f x)))) (f x))))
> (lambda (x) ((lambda (x) (identity (f x)))) (f x))))
> (lambda (x) ((lambda (x) ((f x)))) (f x))))
here is were i get stuck first i want it to go (f(f(x)) but now i will get (lambda x ((f x) (f x)) , the parentheses is certaintly wrong , but i think you understand what i mean. What is wrong with my arguments on how the interpreter works
Your implementation actually delays the further recursion and return a procedure whose body will create copies of itself to fulfill the task at runtime.
Eg. (repeated double 4) ==> (lambda (x) ((repeated double (- 4 1)) (double x)))
So when calling it ((repeated double 4) 2) it runs ((repeated double (- 4 1)) (double 2)))
where the operand part evaluates to (lambda (x) ((repeated double (- 3 1)) (double x))) and so on making the closures at run time so the evaluation becomes equal to this, but in stages during runtime..
((lambda (x) ((lambda (x) ((lambda (x) ((lambda (x) ((lambda (x) (identity x)) (double x))) (double x))) (double x))) (double x))) 2)
A different way of writing the same functionality would be like this:
(define (repeat fun n)
(lambda (x)
(let repeat-loop ((n n)
(x x))
(if (<= n 0)
x
(repeat-loop (- n 1) (fun x))))))
(define (double x) (+ x x))
((repeat double 4) 2) ; ==> 32
You've got a function that takes a function f and an non-negative integer n and returns the function fn, i.e., f(f(f(…f(n)…). Depending on how you think of your recursion, this could be implemented straightforwardly in either of two ways. In both cases, if n is 0, then you just need a function that returns its argument, and that function is the identity function. (This is sort of by convention, in the same way that x0 = 1. It does make sense when it's considered in more depth, but that's probably out of scope for this question.)
How you handle the recursive case is where you have some options. The first option is to think of fn(x) as f(fn-1(x)), where you call f with the result of calling fn-1 with x:
(define (repeated f n)
(if (zero? n)
identity
(lambda (x)
(f ((repeated f (- n 1)) x)))))
The other option is to think of fn(x) as fn-1(f(x)) where _fn-1 gets called with the result of f(x).
(define (repeated f n)
(if (zero? n)
identity
(lambda (x)
((repeated f (- n 1)) (f x)))))
In either case, the important thing to note here is that in Scheme, a form like
(function-form arg-form-1 arg-form-2 ...)
is evaluated by evaluating function-form to produce a value function-value (which should be a function) and evaluating each arg-form-i to produce values arg-value-i, and then calling _function-value_ with the arg-values. Since (repeated ...) produces a function, it's suitable as a function-form:
(f ((repeated f (- n 1)) x))
; |--- f^{n-1} ------|
; |---- f^{n-1}(x) ------|
;|------f(f^{n-1}(x)) ------|
((repeated f (- n 1)) (f x))
; |--- f^{n-1} ------|
;|---- f^{n-1}(f(x))--------|
Based on Will Ness's comment, it's worth pointing out that while these are somewhat natural ways to decompose this problem (i.e., based on the equalities fn(x) = fn-1(f(x)) = f(fn-1(x))), it's not necessarily the most efficient. These solutions both require computing some intermediate function objects to represent fn-1 that require a fair amount of storage, and then some computation on top of that. Computing fn(x) directly is pretty straightforward and efficient with, e.g., repeat:
(define (repeat f n x)
(let rep ((n n) (x x))
(if (<= n 0)
x
(rep (- n 1) (f x)))))
A more efficient version of repeated, then, simply curries the x argument of repeat:
(define (repeated f n)
(lambda (x)
(repeat f n x)))
This should have better run time performance than either of the other implementations.
Danny. I think that if we work repeated with small values of n (0, 1 and 2) will be able to see how the function translates to f(f(f(...(x))). I assume that identity's implementation is (define (identity x) x) (i.e. returns its only parameter as is), and that the "then" part of the if should be (identity f).
(repeated f 0) ;should apply f only once, no repetition
-> (identity f)
-> f
(repeated f 1) ;expected result is f(f(x))
-> (lambda (x) ((repeated f 0) (f x)))
-> (lambda (x) (f (f x))) ;we already know that (repeated f 0) is f
(repeated f 2) ;expected result is f(f(f(x)))
-> (lambda (x) ((repeated f 1) (f x)))
-> (lambda (x) (f (f (f x)))) ; we already know that (repeated f 1) if f(f(x))
... and so on.
Equational reasoning would be very helpful here. Imagine lambda calculus-based language with Haskell-like syntax, practically a combinatory calculus.
Here, parentheses are used just for grouping of expressions (not for function calls, which have no syntax at all – just juxtaposition): f a b c is the same as ((f a) b) c, the same as Scheme's (((f a) b) c). Definitions like f a b = ... are equivalent to (define f (lambda (a) (lambda (b) ...))) (and shortcut for (lambda (a) ...) is (\a-> ...).
Scheme's syntax just obscures the picture here. I don't mean parentheses, but being forced to explicit lambdas instead of just equations and freely shifting the arguments around:
f a b = \c -> .... === f a b c = .... ; `\ ->` is for 'lambda'
Your code is then nearly equivalent to
repeated f n x ; (define (repeated f n)
| n <= 0 = x ; (if (zero? n) identity
| otherwise = repeated f (n-1) (f x) ; (lambda (x)
; ((repeated f (- n 1)) (f x)))))
(read | as "when"). So
repeated f 2 x = ; ((repeated f 2) x) = ((\x-> ((repeated f 1) (f x))) x)
= repeated f 1 (f x) ; = ((repeated f 1) (f x))
= repeated f 0 (f (f x)) ; = ((\y->((repeated f 0) (f y))) (f x))
= f (f x) ; = ((\z-> z) (f (f x)))
; = (f (f x))
The above reduction sequence leaves out the particulars of environment frames creation and chaining in Scheme, but it all works out pretty much intuitively. f is the same f, n-1 where n=2 is 1 no matter when we perform the subtraction, etc..

Y combinator discussion in "The Little Schemer"

So, I've spent a lot of time reading and re-reading the ending of chapter 9 in The Little Schemer, where the applicative Y combinator is developed for the length function. I think my confusion boils down to a single statement that contrasts two versions of length (before the combinator is factored out):
A:
((lambda (mk-length)
(mk-length mk-length))
(lambda (mk-length)
(lambda (l)
(cond
((null? l) 0 )
(else (add1
((mk-length mk-length)
(cdr l))))))))
B:
((lambda (mk-length)
(mk-length mk-length))
(lambda (mk-length)
((lambda (length)
(lambda (l)
(cond
((null? l) 0)
(else (add1 (length (cdr l)))))))
(mk-length mk-length))))
Page 170 (4th ed.) states that A
returns a function when we applied it to an argument
while B
does not return a function
thereby producing an infinite regress of self-applications. I'm stumped by this. If B is plagued by this problem, I don't see how A avoids it.
Great question. For the benefit of those without a functioning DrRacket installation (myself included) I'll try to answer it.
First, let's use some sane (short) variable names, easily trackable by a human eye/mind:
((lambda (h) ; A.
(h h)) ; apply h to h
(lambda (g)
(lambda (lst)
(if (null? lst) 0
(add1
((g g) (cdr lst)))))))
The first lambda term is what's known as little omega, or U combinator. When applied to something, it causes that term's self-application. Thus the above is equivalent to
(let ((h (lambda (g)
(lambda (lst)
(if (null? lst) 0
(add1 ((g g) (cdr lst))))))))
(h h))
When h is applied to h, new binding is formed:
(let ((h (lambda (g)
(lambda (lst)
(if (null? lst) 0
(add1 ((g g) (cdr lst))))))))
(let ((g h))
(lambda (lst)
(if (null? lst) 0
(add1 ((g g) (cdr lst)))))))
Now there's nothing to apply anymore, so the inner lambda form is returned — along with the hidden linkages to the environment frames (i.e. those let bindings) up above it.
This pairing of a lambda expression with its defining environment is known as closure. To the outside world it is just another function of one parameter, lst. No more reduction steps left to perform there at the moment.
Now, when that closure — our list-length function — will be called, execution will eventually get to the point of (g g) self-application, and the same reduction steps as outlined above will again be performed (recalculating the same closure). But not earlier.
Now, the authors of that book want to arrive at the Y combinator, so they apply some code transformations to the first expression, to somehow arrange for that self-application (g g) to be performed automatically — so we may write the recursive function application in the normal manner, (f x), instead of having to write it as ((g g) x) for all recursive calls:
((lambda (h) ; B.
(h h)) ; apply h to h
(lambda (g)
((lambda (f) ; 'f' to become bound to '(g g)',
(lambda (lst)
(if (null? lst) 0
(add1 (f (cdr lst)))))) ; here: (f x) instead of ((g g) x)!
(g g)))) ; (this is not quite right)
Now after few reduction steps we arrive at
(let ((h (lambda (g)
((lambda (f)
(lambda (lst)
(if (null? lst) 0
(add1 (f (cdr lst))))))
(g g)))))
(let ((g h))
((lambda (f)
(lambda (lst)
(if (null? lst) 0
(add1 (f (cdr lst))))))
(g g))))
which is equivalent to
(let ((h (lambda (g)
((lambda (f)
(lambda (lst)
(if (null? lst) 0
(add1 (f (cdr lst))))))
(g g)))))
(let ((g h))
(let ((f (g g))) ; problem! (under applicative-order evaluation)
(lambda (lst)
(if (null? lst) 0
(add1 (f (cdr lst))))))))
And here comes trouble: the self-application of (g g) is performed too early, before that inner lambda can be even returned, as a closure, to the run-time system. We only want it to be reduced when the execution gets to that point inside the lambda expression, after the closure was called. To have it reduced before the closure is even created is ridiculous. A subtle error. :)
Of course, since g is bound to h, (g g) is reduced to (h h) and we're back again where we started, applying h to h. Looping.
Of course the authors are aware of this. They want us to understand it too.
So the culprit is simple — it is the applicative order of evaluation: evaluating the argument before the binding is formed of the function's formal parameter and its argument's value.
That code transformation wasn't quite right, then. It would've worked under normal order where arguments aren't evaluated in advance.
This is remedied easily enough by "eta-expansion", which delays the application until the actual call point: (lambda (x) ((g g) x)) actually says: "will call ((g g) x) when called upon with an argument of x".
And this is actually what that code transformation should have been in the first place:
((lambda (h) ; C.
(h h)) ; apply h to h
(lambda (g)
((lambda (f) ; 'f' to become bound to '(lambda (x) ((g g) x))',
(lambda (lst)
(if (null? lst) 0
(add1 (f (cdr lst)))))) ; here: (f x) instead of ((g g) x)
(lambda (x) ((g g) x)))))
Now that next reduction step can be performed:
(let ((h (lambda (g)
((lambda (f)
(lambda (lst)
(if (null? lst) 0
(add1 (f (cdr lst))))))
(lambda (x) ((g g) x))))))
(let ((g h))
(let ((f (lambda (x) ((g g) x)))) ; here it's OK
(lambda (lst)
(if (null? lst) 0
(add1 (f (cdr lst))))))))
and the closure (lambda (lst) ...) is formed and returned without a problem, and when (f (cdr lst)) is called (inside the closure) it is reduced to ((g g) (cdr lst)) just as we wanted it to be.
Lastly, we notice that (lambda (f) (lambda (lst ...)) expression in C. doesn't depend on any of the h and g. So we can take it out, make it an argument, and be left with ... the Y combinator:
( ( (lambda (rec) ; D.
( (lambda (h) (h h))
(lambda (g)
(rec (lambda (x) ((g g) x)))))) ; applicative-order Y combinator
(lambda (f)
(lambda (lst)
(if (null? lst) 0
(add1 (f (cdr lst)))))) )
(list 1 2 3) ) ; ==> 3
So now, calling Y on a function is equivalent to making a recursive definition out of it:
( y (lambda (f) (lambda (x) .... (f x) .... )) )
=== define f = (lambda (x) .... (f x) .... )
... but using letrec (or named let) is better — more efficient, defining the closure in self-referential environment frame. The whole Y thing is a theoretical exercise for the systems where that is not possible — i.e. where it is not possible to name things, to create bindings with names "pointing" to things, referring to things.
Incidentally, the ability to point to things is what distinguishes the higher primates from the rest of the animal kingdom ⁄ living creatures, or so I hear. :)
To see what happens, use the stepper in DrRacket.
The stepper allows you to see all intermediary steps (and to go back and forth).
Paste the following into DrRacket:
(((lambda (mk-length)
(mk-length mk-length))
(lambda (mk-length)
(lambda (l)
(cond
((null? l) 0 )
(else (add1
((mk-length mk-length)
(cdr l))))))))
'(a b c))
Then choose the teaching language "Intermediate Student with lambda".
Then click the stepper button (the green triangle followed by a bar).
This is what the first step looks like:
Then make an example for the second function and see what goes wrong.

Resources