4  Conditionals

Like all languages, Scheme provides conditionals. The basic form is the if:

(if test-expression
    then-branch
    else-branch)

If test‑expression evaluates to true (i.e., any value other than #f), the “then” branch is evaluated. If not, the “else” branch is evaluated. The “else” branch is optional.

(define p 80)

(if (> p 70) 
    'safe
    'unsafe)
=> safe 

(if (< p 90)
    'low-pressure) ;no “else” branch
=> low-pressure 

Scheme provides some other conditional forms for convenience. They can all be defined as macros (chapter 8) that expand into if-expressions.

4.1  when and unless

when and unless are convenient conditionals to use when only one branch (the “then” or the “else” branch) of the basic conditional is needed.

(when (< (pressure tube) 60)
   (open-valve tube)
   (attach floor-pump tube)
   (depress floor-pump 5)
   (detach floor-pump tube)
   (close-valve tube))

Assuming pressure of tube is less than 60, this conditional will attach floor‑pump to tube and depress it 5 times. (attach and depress are some suitable procedures.)

The same program using if would be:

(if (< (pressure tube) 60)
    (begin
      (open-valve tube)
      (attach floor-pump tube)
      (depress floor-pump 5)
      (detach floor-pump tube)
      (close-valve tube)))

Note that when’s branch is an implicit begin, whereas if requires an explicit begin if either of its branches has more than one form.

The same behavior can be written using unless as follows:

(unless (>= (pressure tube) 60)
   (open-valve tube)
   (attach floor-pump tube)
   (depress floor-pump 5)
   (detach floor-pump tube)
   (close-valve tube))

Not all Schemes provide when and unless. If your Scheme does not have them, you can define them as macros (see chapter 8).

4.2  cond

The cond form is convenient for expressing nested if-expressions, where each “else” branch but the last introduces a new if. Thus, the form

(if (char<? c #\c) -1
    (if (char=? c #\c) 0
        1))

can be rewritten using cond as:

(cond ((char<? c #\c) -1)
      ((char=? c #\c) 0)
      (else 1))

The cond is thus a multi-branch conditional. Each clause has a test and an associated action. The first test that succeeds triggers its associated action. The final else clause is chosen if no other test succeeded.

The cond actions are implicit begins.

4.3  case

A special case of the cond can be compressed into a case expression. This is when every test is a membership test.

(case c
  ((#\a) 1)
  ((#\b) 2)
  ((#\c) 3)
  (else 4))
=> 3

The clause whose head contains the value of c is chosen.

4.4  and and or

Scheme provides special forms for boolean conjunction (“and”) and disjunction (“or”). (We have already seen (section 2.1.1) Scheme’s boolean negation not, which is a procedure.)

The special form and returns a true value if all its subforms are true. The actual value returned is the value of the final subform. If any of the subforms are false, and returns #f.

(and 1 2)  => 2
(and #f 1) => #f

The special form or returns the value of its first true subform. If all the subforms are false, or returns #f.

(or 1 2)  => 1
(or #f 1) => 1

Both and and or evaluate their subforms left-to-right. As soon as the result can be determined, and and or will ignore the remaining subforms.

(and 1 #f expression-guaranteed-to-cause-error)
=> #f

(or 1 #f expression-guaranteed-to-cause-error)
=> 1