(define eternity
  (lambda (x)
    (eternity x)))

(define length0
  (lambda (ls)
    (if (null? ls)
	0
	(add1 (eternity (cdr ls))))))

(define length0
  (lambda (ls)
    ((lambda (f)
       (if (null? ls)
	   0
	   (add1 (f (cdr ls)))))
     eternity)))

(define length1
  (lambda (ls)
    (if (null? ls)
	0
	(add1 (length0 (cdr ls))))))

(define length1
  (lambda (ls)
    (((lambda (f)
	(lambda (ls)
	  (if (null? ls)
	      0
	      (add1 (f (cdr ls))))))
      ((lambda (f)
	(lambda (ls)
	  (if (null? ls)
	      0
	      (add1 (f (cdr ls))))))
       eternity))
     ls)))

(define length2
  (lambda (ls)
    (((lambda (f)
	(lambda (ls)
	  (if (null? ls)
	      0
	      (add1 (f (cdr ls))))))
      ((lambda (f)
	 (lambda (ls)
	   (if (null? ls)
	       0
	       (add1 (f (cdr ls))))))
       ((lambda (f)
	 (lambda (ls)
	   (if (null? ls)
	       0
	       (add1 (f (cdr ls))))))
	eternity)))
     ls)))

(define length3
  (lambda (ls)
    (((lambda (f)
	(lambda (ls)
	  (if (null? ls)
	      0
	      (add1 (f (cdr ls))))))
      ((lambda (f)
	 (lambda (ls)
	   (if (null? ls)
	       0
	       (add1 (f (cdr ls))))))
       ((lambda (f)
	  (lambda (ls)
	    (if (null? ls)
		0
		(add1 (f (cdr ls))))))
       ((lambda (f)
	  (lambda (ls)
	    (if (null? ls)
		0
		(add1 (f (cdr ls))))))
	eternity))))
     ls)))

(define length1
  (lambda (ls)
    (((lambda (g) (g (g eternity)))
      (lambda (f)
	(lambda (ls)
	  (if (null? ls)
	      0
	      (add1 (f (cdr ls)))))))
     ls)))

(define length2
  (lambda (ls)
    (((lambda (g) (g (g (g eternity))))
      (lambda (f)
	(lambda (ls)
	  (if (null? ls)
	      0
	      (add1 (f (cdr ls)))))))
     ls)))

(define length3
  (lambda (ls)
    (((lambda (g) (g (g (g (g eternity)))))
      (lambda (f)
	(lambda (ls)
	  (if (null? ls)
	      0
	      (add1 (f (cdr ls)))))))
     ls)))

(define length3
  ((lambda (g) (g (g (g (g eternity)))))
   (lambda (f)
     (lambda (ls)
       (if (null? ls)
	   0
	   (add1 (f (cdr ls))))))))

;; Here is a little fantasy

(define compose
  (lambda (f g)
    (lambda (x)
      (f (g x)))))

(define compose-lots
  (lambda (g)
    (letrec
      ((loop
	(lambda (n)
	  (if (= n 0)
	      g
	      (compose g (loop (- n 1)))))))
      (loop 100))))

(define length100
  ((lambda (g) ((compose-lots g) eternity))
   (lambda (f)
     (lambda (ls)
       (if (null? ls)
	   0
	   (add1 (f (cdr ls))))))))

(define length
  ((lambda (g) (g g))
   (lambda (f)
     (lambda (ls)
       (if (null? ls)
	   0
	   (add1 ((f f) (cdr ls))))))))

;; g's argument is the function which will be applied if the list is
;; not null. That function is eternity.

(define length0
  ((lambda (g) (g eternity)) ;; g is a function of a function and
   (lambda (f)               ;; returns a function of a list.
     (lambda (ls)
       (if (null? ls)        ;; f is a function of a list.
	   0
	   (add1 (f (cdr ls))))))))

;; if we use g as an argument to g, it doesn't help, because g is not
;; a function of a list.

(define length0
  ((lambda (g) (g g))        ;; g is a function of a function and
   (lambda (f)               ;; returns a function of a list.
     (lambda (ls)
       (if (null? ls)        ;; f is a function of a list.
	   0
	   (add1 (f (cdr ls))))))))

;; g is a function of a function which returns a function of a list.
;; It follows that (g g) is a function of a list.

(define length
  ((lambda (g) (g g))
   (lambda (g)
     (lambda (ls)
       (if (null? ls)
	   0
	   (add1 ((g g) (cdr ls))))))))

;; g is the function, which, given the function which should be called
;; in the non-empty case, can construct the correct recursive function.

(define fact
  ((lambda (g) (g g))
   (lambda (g)
     (lambda (n)
       (if (= n 0)
	   1
	   (* n ((g g) (sub1 n))))))))

(define fact
  ((lambda (g) (g g))
   (lambda (g)
     (lambda (n)
       (if (eq? n 0)
	   1
	   (* n ((g g) (- n 1))))))))

(define fib
  ((lambda (g) (g g))
   (lambda (g)
     (lambda (n)
       (if (<= n 1)
	   1
	   (+ ((g g) (- n 1)) 
	      ((g g) (- n 2))))))))

(define Y
  (lambda (f)
    ((lambda (g) (g g))
     (lambda (g)
       (f (lambda (x) ((g g) x)))))))

(define X
  (lambda (f)
    ((lambda (g) (g (g (g (g (g (g (g (g (g (g (g (g eternity)))))))))))))
     f)))

(define length
  (Y (lambda (f)
       (lambda (ls) 
	 (if (null? ls) 
	     0 
	     (add1 (f (cdr ls))))))))

(define reverse
  (Y (lambda (f)
       (lambda (ls)
	 (if (null? ls)
	     '()
	     (append (f (cdr ls))
		     (list (car ls))))))))

(define append
  (lambda (ls1 ls2)
    (letrec
      ((loop
        (lambda (ls1)
	  (if (null? ls1)
	      ls2
	      (cons (car ls1)
		    (loop (cdr ls1)))))))
      (loop ls1))))

(define append
  (lambda (ls1 ls2)
    (let ((loop
	   (Y (lambda (f)
		(lambda (ls1)
		  (if (null? ls1)
		      ls2
		      (cons (car ls1)
			    (f (cdr ls1)))))))))
      (loop ls1))))

(define append
  (lambda (ls1 ls2)
    ((lambda (loop) (loop ls1))
     (Y (lambda (f)
	  (lambda (ls1)
	    (if (null? ls1)
		ls2
		(cons (car ls1)
		      (f (cdr ls1))))))))))

(define append
  (lambda (ls1 ls2)
    ((Y (lambda (f)
	  (lambda (ls1)
	    (if (null? ls1)
		ls2
		(cons (car ls1)
		      (f (cdr ls1)))))))
     ls1)))

(define odd?
  (lambda (x)
    (if (= x 0)
	#f
	(even? (sub1 x)))))

(define even?
  (lambda (x)
    (if (= x 0)
	#t
	(odd? (sub1 x)))))

(define even?
  ((lambda (f g) (g (f (g (f (g eternity))))))
   (lambda (g)
     (lambda (x)
       (if (= x 0)
	   #f
	   (g (sub1 x)))))
   (lambda (f)
     (lambda (x)
       (if (= x 0)
	   #t
	   (f (sub1 x)))))))

(define even?
  ((lambda (f g) (g f g))
   (lambda (f g)
     (lambda (x)
       (if (= x 0)
	   #f
	   ((g f g) (sub1 x)))))
   (lambda (f g)
     (lambda (x)
       (if (= x 0)
	   #t
	   ((f f g) (sub1 x)))))))
