On this page:
boolean
t
nil
booleanp
if
cond
and
or
not
implies

2 Booleans and Conditionals

type

boolean

value

t : boolean

The boolean representing true.

> (if t 1 2)

1

> (cond (t "a") (t "b"))

"a"

Warning: t happens to be both a boolean and a symbol, so be careful when mixing symbols with booleans.

> (booleanp t)

t

> (symbolp t)

t

value

nil : boolean

The boolean representing false.

> (if nil 1 2)

2

> (cond (nil "a") (t "b"))

"b"

Warning: nil happens to be a boolean, a list, and a symbol, so be careful when mixing lists with booleans, symbols with booleans, or lists with symbols.

> (booleanp nil)

t

> (symbolp nil)

t

> (listp nil)

t

procedure

(booleanp v)  boolean

  v : all
Produces t when v is a boolean (t or nil), produces nil if it’s anything else.

> (booleanp t)

t

> (booleanp nil)

t

> (booleanp 5)

nil

> (booleanp "watermelon")

nil

syntax

(if question-expr then-expr else-expr)

When question-expr is t, if evaluates and returns then-expr. When question-expr is nil, it evaluates and returns else-expr.

> (if t 1 2)

1

> (if nil 1 2)

2

It evaluates the two branches lazily, meaning it doesn’t evaluate then-expr unless the question really is t, and it doesn’t evaluate else-expr unless the question really is nil. So this division-by-zero expression never gets evaluated.

> (if nil (/ 1 0) "the-other-thing")

"the-other-thing"

> (if t "the-first-thing" (/ 1 0))

"the-first-thing"

This makes if (and similar forms like cond) useful for guarding function calls against bad data.

> (defunc try-/ (x y)
    :input-contract (and (rationalp x) (rationalp y))
    :output-contract t
    (if (not (equal y 0))
        ; this division is only evaluated when y isn't 0
        (/ x y)
        nil))
> (try-/ 2 6)

1/3

> (try-/ 2 0)

nil

This delay in evaluating the branches is also important for functions that use recursion, so it doesn’t evaluate the recursive call when it should be on the base case.

syntax

(cond (question-expr answer-expr) ... (t else-expr))

Starts evaluating each question-expr in order. If a question produces t, cond evaluates and returns the answer-expr associated with it. If a question produces nil, it moves on to the next question. If all of the questions are nil, it evaluates and returns else-expr.

> (cond (nil 1)
        (t 2)
        (t 3))

2

> (cond (nil 1)
        (nil 2)
        (t 3))

3

Like if, cond evaluates its answer expressions lazily, which means it can be used to guard against bad data, or to guard recursive calls.

> (cond (nil (/ 1 0)) (t "the-other-thing"))

"the-other-thing"

> (cond (t "the-first-thing") (t (/ 1 0)))

"the-first-thing"

> (defunc try-/ (x y)
    :input-contract (and (rationalp x) (rationalp y))
    :output-contract t
    (cond ((not (equal y 0))
           ; this division is only evaluated when y isn't 0
           (/ x y))
          (t ; else
           nil)))
> (try-/ 3 6)

1/2

> (try-/ 3 0)

nil

syntax

(and conjunct-expr ...)

Produces t if all the conjunct-exprs are t.

> (and t t)

t

> (and t nil)

nil

> (and nil t)

nil

> (and nil nil)

nil

> (and t t t t t)

t

> (and t t t nil t)

nil

Like if and cond, and evaluates its arguments lazily, only when all of the previous arguments have praduced t. So this division by zero expression never gets evaluated:

> (and nil (/ 1 0))

nil

syntax

(or disjunct-expr ...)

Produces t if at least one of the conjunct-exprs is t.

> (or t t)

t

> (or t nil)

t

> (or nil t)

t

> (or nil nil)

nil

> (or nil nil nil nil nil)

nil

> (or nil nil nil t nil)

t

Like if and cond, or evaluates its arguments lazily, only when none of the previous arguments have praduced t. So this division by zero expression never gets evaluated:

> (or t (/ 1 0))

t

procedure

(not b)  boolean

  b : boolean
Produces nil if b is t, and t if b is nil.

> (not t)

nil

> (not nil)

t

syntax

(implies ante-expr conseq-expr)

> (implies t t)

t

> (implies t nil)

nil

> (implies nil t)

t

> (implies nil nil)

t

> (test? (implies (natp n) (<= n (* n n))))