SICP Exercise 1.6

Question

Alyssa P. Hacker doesn’t see why if needs to be provided as a special form. “Why can’t I just define it as an ordinary procedure in terms of cond?” she asks. Alyssa’s friend Eva Lu Ator claims this can indeed be done, and she defines a new version of if:

(define (new-if predicate
                then-clause
                else-clause)
  (cond (predicate then-clause)
        (else else-clause)))

Eva demonstrates the program for Alyssa:

(new-if (= 2 3) 0 5)
5

(new-if (= 1 1) 0 5)
0

Delighted, Alyssa uses new-if to rewrite the square-root program:

(define (sqrt-iter guess x)
  (new-if (good-enough? guess x)
          guess
          (sqrt-iter (improve guess x) x)))

What happens when Alyssa attempts to use this to compute square roots? Explain.

Answer

If you actually try running this code, you will find that the program enters an endless loop and does not terminate.

To understand why this happens, remember that the Lisp interpreter uses applicative-order evaluation. This means that for each procedure, the operator and all operands will be evaluated before the operation is carried out. As we can see in the code example given above, the sqrt-iter procedure itself is passed to new-if as its else-clause. This has the consequence of each iteration of sqrt-iter calling another iteration of itself, which creates an endless loop.

So why does this not happen with the regular if-statement?

Well, the simple answer is: because the Lisp developers said so. if is a special form in Lisp. Special forms often behave differently from regular procedures. In the case of if, it was specifically written to not use applicative-order evaluation. Instead, it only evaluates the appropriate branch. This is why the following code does not generate an error, even though it contains an illegal operation (division by zero):

(if #t
  1
  (/ 1 0))
; outputs 1