Note that if/cond
are syntax, not procedures. There is a difference in using the if
syntax, and actually defining it and trying to use it. To illustrate, consider the following:
(+ 1 (- 4 2))
Here we have the +
procedure, that takes zero or more numbers and returns their sum. We have applied the procedure with two arguments, the second being an expression (- 4 2)
instead of a value. Notice that this will not result in a contract violation error, and actually evaluate to 3. Why? Because any argument which is not a value is first simplified to a value. So the expression evaluates as:
(+ 1 (- 4 2))
=> (+ 1 2)
=> 3
Coming back to your defined if
procedure, the same rules apply, in that any arguments passed to the function, that are not values themselves, will be reduced to values before using the if
procedure. So the evaluation for (factorial 1)
becomes:
(factorial 1)
=> (if (= 1 1) 1 (* 1 (factorial 0)))
=> (if #t 1 (* 1 (factorial 0)))
=> (if #t 1 (* 1 (if (= 0 1) 1 (* 0 (factorial -1)))))
=> (if #t 1 (* 1 (if #f 1 (* 0 (factorial -1)))))
=> ... (endless loop to negative infinity)
This is different from using the if
syntax, where evaluation is short circuited, meaning that the else
case is only evaluated if the predicate is false. For example,
(if #t 1 (+ 1 'a))
will not throw an error for trying to add a number and a symbol, because it will not evaluate the else-expression in the first place (since predicate evaluates to true). Hence, it returns 1.