Chapter 3 - Essential Lisp in Twelve Lessons

Lesson 2 - Essential Evaluation

A form is meant to be evaluated

A form can be either an atom or a list. The important thing is that the form is meant to be evaluated. Evaluation has a fairly technical definition that we'll gradually expose in this section.

Evaluation is simple if the form is an atom. Lisp treats the atom as a name, and retrieves the value for the name (if a value exists). You probably wonder why I'm avoiding the more direct explanation of calling the atom a variable. The reason is that the atom can have either a variable value or a constant value. And the atom's value can be constant for a couple of reasons.

A number is an atom. (Its value is constant for obvious reasons.) Lisp does not store a value for a number -- the number is said to be self-evaluating.

We're going to introduce a new term without a complete definition. For now, think of a symbol as an atom that can have a value. We'll look at symbols in greater detail when we get to Lesson 5.

A symbol defined in a defconstant form has a constant value. Lisp will store the value as if the atom had a variable value, and add a note to the effect that the value is not allowed to change.

A symbol in the KEYWORD package is self-evaluating. We'll look at packages in detail in Chapter 31. For now, all you need to know is that a symbol beginning with the : character (called the package prefix) is a keyword symbol. Keyword symbols have themselves as their values.

A symbol can get a variable value in many different ways. Lisp actually keeps several different values for a symbol. One has the traditional meaning as the value of the symbol taken as a variable. Another has meaning as the symbol's function. Still others keep track of the symbol's documentation, its printed representation, and properties that the programmer chooses to associate with the symbol. We'll explore some of these in more detail in Lesson 5, Lesson 6, and Lesson 7.

If a form is a list, then the first element must be either a symbol or a special form called a lambda expression. (We won't look at lambda expressions for a while.) The symbol must name a function. In Lisp, the symbols +, -, *, and / name the four common arithmetic operations: addition, subtraction, multiplication, and division. Each of these symbols has an associated function that performs the arithmetic operation.

So when Lisp evaluates the form (+ 2 3), it applies the function for addition to the arguments 2 and 3, giving the expected result 5. Notice how the function symbol, +, precedes its arguments. This is prefix notation. Any time you see a list, look to its first element to find out what Lisp will do to evaluate the list as a form.

A function is applied to its arguments

Lisp, when given a list to evaluate, treats the form as a function call. We'll be looking a lot at Lisp evaluation from now on, so we'll use some visual aids to identify the input to Lisp and its responses:

? the Lisp prompt precedes input to Lisp
-> result of Lisp evaluation

For example:

? (+ 4 9)
-> 13

? (- 5 7)
-> -2

? (* 3 9)
-> 27

? (/ 15.0 2)
-> 7.5

In each case above, the evaluated form is a list. Its first element is a symbol, which names a function. The remaining elements are arguments of the function. Here, the arguments are all numbers, and we know that numbers are self-evaluating.

Here are a few more examples:

? (atom 123)
-> T

? (numberp 123)
-> T

? (atom :foo)
-> T

? (numberp :foo)
-> NIL

ATOM and NUMBERP are predicates. Predicates return a true or false value. NIL is the only false value in Lisp -- everything else is true. Unless a predicate has a more useful value to return, it conventionally returns T to mean true. ATOM returns T if its one argument is a Lisp atom. NUMBERP returns T if its argument is a number.

To evaluate each of the above forms, Lisp first evaluates the arguments (from left to right), then evaluates the first element to get its function, then applies the function to the arguments. With only a handful of exceptions, which we'll learn about at the end of this lesson, Lisp always does the same thing to evaluate a list form:

  1. Evaluate the arguments, from left to right.
  2. Get the function associated with the first element.
  3. Apply the function to the arguments.

Remember that an atom can also be a Lisp form. When given an atom to evaluate, Lisp simply returns its value:

? 17.95
-> 17.95

? :A-KEYWORD
-> :A-KEYWORD

? *FEATURES*
-> (:ANSI-CL :CLOS :COMMON-LISP)

? "Hello, world!"
-> "Hello, world!"

? WHAT-IS-THIS?
-> Error: Unbound variable

Numbers and keywords are self-evaluating. So are strings. The *FEATURES* variable is predefined by Lisp -- your system will probably return a different value.

The symbol WHAT-IS-THIS? doesn't have a value, because it's not predefined by Lisp, and I haven't given it a value. The system responds with an error message, rather than a value. We mark the message with ->| rather than the -> marker we use for successful evaluations. Your system will probably print a different message.

A function can return any number of values

Sometimes you'd like to have a function return several values. For example, a function which looks up a database entry might return both the desired result and a completion status code. One way to do this is to pass to the function a location for one of the results; this is possible, but very uncommon for a Lisp program.

Another approach creates a single return value to combine both the result and the status code. Lisp gives you several different ways to do this, including structures. Experienced Lisp programmers don't do this when the created value will just be taken apart into its components and then forgotten, since the composite value then becomes garbage (see Chapter 29) that eventually slows down the operation of the program.

The right way to return multiple values from a function is to use the VALUES form. We'll see VALUES used in the context of a function in a little while. For now, let's see what happens when Lisp evaluates a VALUES form:

? (values 1 2 3 :hi "Hello")
-> 1
-> 2
-> 3
-> :HI
-> "Hello"

Notice how Lisp returned a value (following the -> indicator) for each argument to the VALUES form. My Lisp system represents this by printing each value on a new line; yours may separate the values some other way.

Arguments are usually not modified by a function

I mentioned earlier that you can pass a location to a function, and have the function change the location's value. This is a very uncommon practice for a Lisp program, even though other languages make it part of their standard repertoire.

You could specify the location to be modified as either a non-keyword symbol or a composite value -- obviously, you can't modify a constant. If you provide a symbol, then your function must execute code to give the symbol a new value. If you provide a composite data structure, your function must execute code to change the correct piece of the composite value. It's harder to write Lisp code to do this, and it's harder to understand programs written this way. So Lisp programmers usually write functions that get their inputs from parameters, and produce their outputs as the function result.

Arguments are usually evaluated before function application

When Lisp evaluates a function, it always evaluates all the arguments first, as we saw earlier. Unfortunately, every rule has exceptions, and this rule is no exception (as we'll soon see)... The problem is not that Lisp doesn't always evaluate a function's arguments, but that not every list form is a function call.

Arguments are evaluated in left-to-right order

When a list form is a function call, its arguments are always evaluated in order, from left to right. As in other programming languages, it's in poor taste to rely on this, but if you absolutely have to rely on the order, it's good to know that Lisp defines it for you.

Special forms and macros change argument evaluation

So if a list form isn't always a function call, what else can it be? There are two cases, but the result is the same: some arguments are evaluated, and some aren't. Which is which depends upon the form and nothing else. You'll just have to learn the exceptions. Fortunately, most Lisp systems will show you the online documentation for any form with just a keystroke or two.

There are two kinds of forms that don't evaluate all of their arguments: special forms and macros. Lisp predefines a small number of special forms. You can't add your own special forms -- they're primitive features of the language itself. Lisp also defines quite a few macros. You can also define your own macros. Macros in Lisp let you use the full power of the language to add your own features. Later in this chapter we'll look briefly at how to define simple macros. In Chapter 20 we'll cover topics surrounding the creation of more complex macros.


Contents | Cover
Chapter 2 | Chapter 4 | Chapter 3, Lesson 1 | Chapter 3, Lesson 3 | Chapter 3, Introduction
Copyright © 1995-1999, David B. Lamkins
All Rights Reserved Worldwide

This book may not be reproduced without the written consent of its author. Online distribution is restricted to the author's site.