February 16, 2013

SICP Exercise 1-6 : Understanding order of evaluation

SICP Exercise 1.6 is very interesting. I found it difficult to understand the problem and then the solution. It was fun to actually understand and reason about the solution.

In section 1.1.7 we define a function to calculate square root of a number by Newton’s method of successive approximation.

``` scheme Newton’s method of successive approximation for finding square root of a number

(define (square x) (* x x))

(define (average x y) (/ (+ x y) 2))

(define (improve guess x) (average guess (/ x guess)))

(define (good-enough? guess x) (< (abs (- x (square guess))) 0.0000000001))

(define (square-root-iter guess x) (if (good-enough? guess x) guess (square-root-iter (improve guess x) x)))


Exercise 1.6 defines a replacement for special form `if` as follows


``` scheme 'if' as a ordinary procedure

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

Now square-root-iter function changed as follows

``` scheme New version of ‘square-root-iter’

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


The problem asks us, what is the result of this version of
`square-root-iter`?

Definition of `new-if` is correct and it can be tested with some test
cases.

``` scheme

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

But still evaluation of square-root-iter with new-if results in infinite loop. Why?

The answer is order of evaluation.

In scheme, there is a general rule of evaluation. First value inside parenthesis is considered as function and all other values are passed as arguments to that function. Only Special forms are evaluated differently.

if is a special form which is evaluated in following way.

(if <predicate> <consequent> <alternative>)

First, <predicate> is evaluated. If it is true, then <consequent> is evaluated. Otherwise <alternative> is evaluated. But both <consequent> and <alternative> are never evaluated at the same time.

Our new-if is not a special form. So it will be evaluated as any other function following applicative order of evaluation.

Applicative order of evaluation first evaluates all operands completely and then passed them to operator. There is also normal order evaluation in which operands are evaluated when they are actually needed.

Scheme interpreter uses applicative order. So in new version of square-root-iter, while evaluating new-if it will try to evaluate it’s operands first. But the second operand of new-if is recursive call to square-root-iter. So this evaluation will never finish.

Problem is not in implementation of new-if but in it’s evaluation.