Why is if a special form in Scheme?

The if conditional is a special form in Scheme and Lisp. As an exercise questionin SICP asks:

  1. Can if be implemented as a procedure that uses the cond special form?
  2. Is the if special form necessary in scheme, when the cond special form exists?

The need for special forms arises in general because of how procedures are evaluated in Scheme. R6RS the latest Scheme specification descibes how a procedure call is made section 9.1, Procedure calls:

When a procedure call is evaluated, the operator and operand expressions are evaluated (in an unspecified order) and the resulting procedure is passed the resulting arguments.

However the evaluation of the if special form differs from this. R6RS specfies how the if special form is evaluated in section 11.4 of the specification:

(if <test> <consequent> <alternate>)
(if <test> <consequent>)
Syntax: <test>, <consequent>, and <alternate> must be expressions.

Semantics: An if expression is evaluated as follows: first, <test> is evaluated. If it yields a true value(see section 5.7), then <consequent> is evaluated and its values are returned. Otherwise <alternate> is evaluated and its values are returned. If <test> yields #f and no <alternate> is specified, then the result of the expression is unspecified.

i.e. unlike in a procedure call, not all parameters are evaluated. Whether the <consequent> or lt;alternate> parameter is evaluated depends on whether or not the evaluation of the <test> paremeter results in the value #t (true.) This behavior cannot be replicated with a procedure call. It needs special support from the language interpreter/compiler.

For example (as described in Section 2.7 of The Scheme Programming Language,) consider a procedure that computes the reciprocal of an integer, without failing when the number is zero.

(define (reciprocal n)
  (if (not (= 0 n))
    (/ 1 n)

Suppose we define if as a procedure as below:

(define (new-if predicate consequent alternative)
    (predicate consequent)
    (else alternative)))

Suppose further that the =if= special form is replaced with the new-if procedure as shown below:

(define (reciprocal n)
  (new-if (not (= 0 n))
    (/ 1 n)

Unlike the code that uses the if special form, the code above will fail with a divide by zero error. This is because the new-if procedure is called only after its parameters are evaluated. Hence (/ 1 n) is executed, resulting in a divide-by-zero.

Hence the need for if to be a special form.

It could be argued that using some combination of quoting and lambdas, one could make an if procedure. This wonderful note shows that if the inputs to the if conditional procedure are closures, one could replace the if special form in Scheme (but not Lisp &emdash; the difference boils down to lexical scoping used in Scheme vs dynamic scoping used in Lisp.) However, the syntactic inconvenience would be so great that even Scheme defines if as a special form.

Is the if special form necessary, given that the cond special form already exists? Not really. But the convenience of the if special form and the difficulty of implementing it as a procedure may have caused the designers of Scheme to make it a special form in the language.