Common Lisp the Language, 2nd Edition

Next: Blocks and Exits Up: Control Structure Previous: Establishing New Variable

# 7.6. Conditionals

The traditional conditional construct in Lisp is `cond`. However, `if` is much simpler and is directly comparable to conditional constructs in other programming languages, so it is considered to be primitive in Common Lisp and is described first. Common Lisp also provides the dispatching constructs `case` and `typecase`, which are often more convenient than `cond`.

[Special Form]
`if test then [else]`

The `if` special form corresponds to the if-then-else construct found in most algebraic programming languages. First the form test is evaluated. If the result is not `nil`, then the form then is selected; otherwise the form else is selected. Whichever form is selected is then evaluated, and `if` returns whatever is returned by evaluation of the selected form.

    (if test then else) == (cond (test then) (t else))

but `if` is considered more readable in some situations.

The else form may be omitted, in which case if the value of test is `nil` then nothing is done and the value of the `if` form is `nil`. If the value of the `if` form is important in this situation, then the `and` construct may be stylistically preferable, depending on the context. If the value is not important, but only the effect, then the `when` construct may be stylistically preferable.

[Macro]
`when test {form}*`

`(when test form1 form2 ... )` first evaluates test. If the result is `nil`, then no form is evaluated, and `nil` is returned. Otherwise the forms constitute an implicit `progn` and are evaluated sequentially from left to right, and the value of the last one is returned.

    (when p a b c) == (and p (progn a b c))
    (when p a b c) == (cond (p a b c))
    (when p a b c) == (if p (progn a b c) nil)
    (when p a b c) == (unless (not p) a b c)

As a matter of style, `when` is normally used to conditionally produce some side effects, and the value of the `when` form is normally not used. If the value is relevant, then it may be stylistically more appropriate to use `and` or `if`.

[Macro]
`unless test {form}*`

`(unless test form1 form2 ... )` first evaluates test. If the result is not `nil`, then the forms are not evaluated, and `nil` is returned. Otherwise the forms constitute an implicit `progn` and are evaluated sequentially from left to right, and the value of the last one is returned.

    (unless p a b c) == (cond ((not p) a b c))
    (unless p a b c) == (if p nil (progn a b c))
    (unless p a b c) == (when (not p) a b c)

As a matter of style, `unless` is normally used to conditionally produce some side effects, and the value of the `unless` form is normally not used. If the value is relevant, then it may be stylistically more appropriate to use `if`.

[Macro]
`cond {(test {form}*)}*`

A `cond` form has a number (possibly zero) of clauses, which are lists of forms. Each clause consists of a test followed by zero or more consequents. For example:

    (cond (test-1 consequent-1-1 consequent-1-2 ...)
          (test-2)
          (test-3 consequent-3-1 ...)
          ... )

The first clause whose test evaluates to non-`nil` is selected; all other clauses are ignored, and the consequents of the selected clause are evaluated in order (as an implicit `progn`).

More specifically, `cond` processes its clauses in order from left to right. For each clause, the test is evaluated. If the result is `nil`, `cond` advances to the next clause. Otherwise, the cdr of the clause is treated as a list of forms, or consequents; these forms are evaluated in order from left to right, as an implicit `progn`. After evaluating the consequents, `cond` returns without inspecting any remaining clauses. The `cond` special form returns the results of evaluating the last of the selected consequents; if there were no consequents in the selected clause, then the single (and necessarily non-null) value of the test is returned. If `cond` runs out of clauses (every test produced `nil`, and therefore no clause was selected), the value of the `cond` form is `nil`.

If it is desired to select the last clause unconditionally if all others fail, the standard convention is to use `t` for the test. As a matter of style, it is desirable to write a last clause `(`t` `nil`)` if the value of the `cond` form is to be used for something. Similarly, it is in questionable taste to let the last clause of a `cond` be a ``singleton clause''; an explicit `t` should be provided. (Note moreover that `(cond ... (x))` may behave differently from `(cond ... (`t` x))` if x might produce multiple values; the former always returns a single value, whereas the latter returns whatever values x returns. However, as a matter of style it is preferable to obtain this behavior by writing `(cond ... (t (values x)))`, using the `values` function explicitly to indicate the discarding of any excess values.) For example:

    (setq z (cond (a 'foo) (b 'bar)))          ;Possibly confusing
    (setq z (cond (a 'foo) (b 'bar) (t nil)))  ;Better
    (cond (a b) (c d) (e))                     ;Possibly confusing
    (cond (a b) (c d) (t e))                   ;Better
    (cond (a b) (c d) (t (values e)))          ;Better (if one value
                                               ; needed)
    (cond (a b) (c))                           ;Possibly confusing
    (cond (a b) (t c))                         ;Better
    (if a b c)                                 ;Also better

A Lisp `cond` form may be compared to a continued if-then-else as found in many algebraic programming languages:

    (cond (p ...)                            if p then ...
          (q ...)            roughly         else if q then ...
          (r ...)          corresponds       else if r then ...
          ...                   to           ...
          (t ...))                           else ...

[Macro]
`case` `keyform` `{({({key}*)` `|` `key}` `{form}*)}*`

`case` is a conditional that chooses one of its clauses to execute by comparing a value to various constants, which are typically keyword symbols, integers, or characters (but may be any objects). Its form is as follows:

    (case keyform
      (keylist-1 consequent-1-1 consequent-1-2 ...)
      (keylist-2 consequent-2-1 ...)
      (keylist-3 consequent-3-1 ...)
      ...)

Structurally `case` is much like `cond`, and it behaves like `cond` in selecting one clause and then executing all consequents of that clause. However, `case` differs in the mechanism of clause selection.

The first thing `case` does is to evaluate the form keyform to produce an object called the key object. Then `case` considers each of the clauses in turn. If key is in the keylist (that is, is `eql` to any item in the keylist) of a clause, the consequents of that clause are evaluated as an implicit `progn`; `case` returns what was returned by the last consequent (or `nil` if there are no consequents in that clause). If no clause is satisfied, `case` returns `nil`.

The keys in the keylists are not evaluated; literal key values must appear in the keylists. It is an error for the same key to appear in more than one clause; a consequence is that the order of the clauses does not affect the behavior of the `case` construct.

Instead of a keylist, one may write one of the symbols `t` and `otherwise`. A clause with such a symbol always succeeds and must be the last clause (this is an exception to the order-independence of clauses). See also `ecase` and `ccase`, each of which provides an implicit `otherwise` clause to signal an error if no clause is satisfied.

If there is only one key for a clause, then that key may be written in place of a list of that key, provided that no ambiguity results. Such a ``singleton key'' may not be `nil` (which is confusable with `()`, a list of no keys), `t`, `otherwise`, or a cons.

Compatibility note: The Lisp Machine Lisp `caseq` construct uses `eq` for the comparison. In Lisp Machine Lisp `caseq` therefore works for fixnums but not bignums. The MacLisp `caseq` construct simply prohibits the use of bignums; indeed, it permits only fixnums and symbols as clause keys. In the interest of hiding the fixnum-bignum distinction, and for general language consistency, `case` uses `eql` in Common Lisp.

The Interlisp `selectq` construct is similar to `case`.

[Macro]
`typecase` `keyform` `{(type` `{form}*)}*`

`typecase` is a conditional that chooses one of its clauses by examining the type of an object. Its form is as follows:

    (typecase keyform
      (type-1 consequent-1-1 consequent-1-2 ...)
      (type-2 consequent-2-1 ...)
      (type-3 consequent-3-1 ...)
      ...)

Structurally `typecase` is much like `cond` or `case`, and it behaves like them in selecting one clause and then executing all consequents of that clause. It differs in the mechanism of clause selection.

The first thing `typecase` does is to evaluate the form keyform to produce an object called the key object. Then `typecase` considers each of the clauses in turn. The type that appears in each clause is a type specifier; it is not evaluated but is a literal type specifier. The first clause for which the key is of that clause's specified type is selected, the consequents of this clause are evaluated as an implicit `progn`, and `typecase` returns what was returned by the last consequent (or `nil` if there are no consequents in that clause). If no clause is satisfied, `typecase` returns `nil`.

As for `case`, the symbol `t` or `otherwise` may be written for type to indicate that the clause should always be selected. See also `etypecase` and `ctypecase`, each of which provides an implicit `otherwise` clause to signal an error if no clause is satisfied.

It is permissible for more than one clause to specify a given type, particularly if one is a subtype of another; the earliest applicable clause is chosen. Thus for `typecase`, unlike `case`, the order of the clauses may affect the behavior of the construct. For example:

    (typecase an-object
       (string ...)            ;This clause handles strings
       ((array t) ...)         ;This clause handles general arrays
       ((array bit) ...)       ;This clause handles bit arrays
       (array ...)             ;This handles all other arrays
       ((or list number) ...)  ;This handles lists and numbers
       (t ...))                ;This handles all other objects

A Common Lisp compiler may choose to issue a warning if a clause cannot be selected because it is completely shadowed by earlier clauses.

Next: Blocks and Exits Up: Control Structure Previous: Establishing New Variable