Go to the first, previous, next, last section, table of contents.

cond

In most procedural programming languages, you can write a sequence of if tests using an extended version of if, something like this:

if test1 then
   action1();
else if test2 then
   action2();
else if test3 then
   action3();
else
   action4();

Scheme has a similar construct, a special form called cond. The above example might be written in Scheme as

(cond (test1
       (action1))
      (test2
       (action2))
      (test3
       (action3))
      (else
       (action4)))

Notice that each test-and-action pair is enclosed in parentheses. In this example, test1 is just a variable reference, not a procedure call, i.e., we're testing to see if the value of the variable test1 is #f; if not, we'll execute (action1), i.e., call the procedure action1. If it is false, control "falls through" to the next test, and keeps going until one of the tests evaluates to a true value (anything but #f).

Notice that we indent the actions corresponding to a test by one character. This lines the actions up directly under the tests, rather than under the opening parenthesis that groups them together.

The else clause of a cond is optional; if present, that branch will be taken "by default"---if none of the other conditions evaluates to a true value, the else branch will be taken.

We don't really need the else clause, because we could get the same effect by using a test expression that always evaluates to a true value. One way of doing this is to use the literal #t, the true boolean, because it's always true.

(cond (test1
       (action1))
      (test2
       (action2))
      (test3
       (action3))
      (#t            ; literal #t is always true, so
       (action4)))   ; this branch is taken if we get this far

The code above is equivalent to a nested set of if expressions:

(if test1
    (action1)
    (if test2
        (action2)
        (if test3
            (action3)
            (if #t
                (action4)))))

Like an if, a cond returns the value of whatever "branch" it executes. If test1 is true, for example, the above cond will return the value returned from the procedure call (action1).

Remember that each branch of an if is a single expression; if you want to execute more than one expression in a branch, you have to wrap the expressions in a begin. With cond, you don't have to do this. You can follow a test expression with more than one action expression, and Scheme will evaluate all of them, in order, and return the value of the last one, just like a begin or a procedure body.

Suppose we want to modify the above cond example so that it prints out the branch it's taking, as well as evaluating the action expression and returning its value. We can do this:

(cond (test1
       (display "taking first branch")
       (action1))
      (test2
       (display "taking second branch")
       (action2))
      (test3
       (display "taking third branch")
       (action3))
      (else
       (display "taking fourth (default) branch")
       (action4)))

This cond will return the same value as the original, because it always returns the value of the last expression in a branch. As it executes, however, it also displays what it's doing. We can use the cond both for value and for effect.

Be particularly careful about parentheses with cond. You must enclose each branch with a pair of parentheses around the test expression and the corresponding sequence of action expressions. If you want to call a procedure in any of those expressions, you must also put parentheses around the procedure call. In the above example, if we wanted the first test to be a call to a procedure test1---rather than just fetching the value of the variable test1---we'd write

(cond ((test1)
       (display "taking first branch")
       (action1))
      ...)

instead of

(cond (test1
       (display "taking first branch")
       (action1))
      ...)

(Note the indenting here. We usually line up a test and the corresponding sequence of actions vertically, whether or not the expression starts with a parentheses. That is, we indent one space past the opening parenthesis of the pair of parentheses that goes around them all.)

The "extra" parentheses are necessary so that cond can tell which action sequences are grouped with which tests.

Don't be afraid to use cond for conditionals with only one or two branches. cond is often more convenient than if because it can execute a sequence of expressions, instead of just one. It's not uncommon to see things like this:

...
(cond ((foo)
       (bar)
       (baz)))
...

Don't be confused by this--there's only one branch to this cond, like a one-branch if. We could have written it

...
(if (foo)
    (begin (bar)
           (baz)))
...

It's just more convenient to use cond so that we can call bar before calling baz and returning its result, without explicitly writing a begin expression to sequence them.

We say that cond is syntactic sugar for nested ifs with begins around the branches. There's nothing we can do with cond that we can't do straightforwardly with if and begin---cond just gives us a "sweetened" syntax, i.e., one that's more convenient.

Most of the special forms in Scheme are like this--they're just a convenient way of writing things that you could write using more basic special forms. (There are only five "core" special forms that are really necessary, and the others are equivalent to combinations of those special forms.)


Go to the first, previous, next, last section, table of contents.