1

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?

Will Ness
  • 62,652
  • 8
  • 86
  • 167
hawkeye
  • 31,052
  • 27
  • 133
  • 271
  • Actually you don't need `apply`. This version works just as fine `(define Y (lambda (f) ((lambda (x) (x x)) (lambda (g) (f (lambda (a) ((g g) a)))))))` In the sample of Racket code it uses a lazy module for evaluation. I don't know how it works but this is the difference I found between the two samples. – Ivancho Sep 30 '14 at 10:56

2 Answers2

11

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.)

Eli Barzilay
  • 28,131
  • 3
  • 62
  • 107
0

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))))))
Alaya
  • 2,895
  • 3
  • 20
  • 35