CCRMA A quick tour of LispLand

(and a glimpse of some of its life forms)


by Fernando Lopez Lezcano (nando@ccrma.stanford.edu)
© 1991-1996, All rights reserved, CCRMA, Stanford University

Introduction

This document tries to give some basic guidelines on the use of the Common Lisp language environment. Both CLM (Common Lisp Music) and CM (Common Music) are written in Common Lisp and in fact when you are using them you are programming in Lisp! Although it is not necessary to be a Lisp wizard to use CLM or CM, a little knowledge of Lisp syntax will help you get the most out of the tools you will be working with. In addition to this short introduction, you will probably want to read through an introductory book on LISP. There are a number of such books available. A good one is "Common Lisp: A gentle introduction to symbolic computation" by Touretzky, another is "LISP" by Winston (this is a large book that ranges from the basic to the advanced; you need only read the first part of it.) Finally, once you become proficient in LISP, you're sure to want to have a copy of the bible of Common Lisp, "Common LISP, the Language" by Guy Steele.

What is Lisp?

Lisp as a computer language is basically an interpreter. In the context of out introductory course we can say that an interpreter is a computer program that waits for some command from the user, executes it and waits again for further commands. The prompt or invitation to type a command is slightly different for each implementation of Common Lisp. In Allegro Common Lisp 4.3 (running on Linux or Irix) it is "USER(1): ", in Alegro Common Lisp 3.1.20 (running on NeXT black hardware) it is "<cl> " and in Gnu Common Lisp 1.1 (running on NextStep in Intel hardware) it is: "> ". If after getting the prompt you type in a command, Lisp evaluates it, displays the result of the evaluation and then prints again the prompt and waits for further commands. Before you start typing at the prompt you should know some of the very few rules that Lisp uses to understand what you type.

- Note:
If you want to try the small examples included in this document you can start now Common Lisp. How to do that depends on the platform you are currently logged in. The recommended program to run lisp is emacs or xemacs. Take a look at this instructions on how to do that.

Interpreting commands

In response to the lisp prompt, you can either type commands or evaluate LISP forms (which we will shortly see in more detail). A command always begins with a colon and performs some specific action regarding the state of the Lisp interpreter (this commands are specific to Allegro Common Lisp, you won't find them in Gnu Common Lisp). The most common commands that you will probably use are:

	:help - Prints a list of all available commands.
	:help <command> - Prints more detailed help on a specific command.
	:ld <file>  - Loads a Lisp file. 
	:cf <file> - Compiles a Lisp file.
	:cl <file> - Compiles and loads a Lisp file.
	:reset - Resets the Lisp interpreter after an error has occurred.

Evaluating forms

A form in Lisp parlance is anything you submit to the interpreter to be evaluated as a program, and is meant to produce one or more values as a result. To evaluate a form simply type it at the lisp prompt and press the return key. Lisp will print the result of the evaluation and then a new prompt. A form can be as simple as a number, and numbers in Lisp always evaluate to themselves as in the following example:

<cl> 12	        <--- form typed at the lisp prompt : just a number
12		<--- the lisp interpreter prints the result of the evaluation
<cl>		<--- you get a new prompt
Symbols are also forms, and by themselves they are usually associated with variable names. The results of evaluating a symbol that is bound to a variable is the value of that variable (to bind in Lisp means roughly to associate a symbol or name with a place where something can be stored):

<cl> *common-music-syntaxes*
(:CLM :MUSICKIT :MIDI :CSOUND) 
<cl> 
The third and most important kind of forms are the....

Lists

Almost everything in Lisp is a list (the name "lisp" actually comes from "list processor" and not "lots of insipid and silly parenthesis" as some people may suggest). And a list is nothing more than a set of elements separated by spaces and enclosed in parenthesis. The elements themselves can be numbers, symbols or even other lists, as in the following examples:

  (peter john williams)
  (1 2 3)
  (strawberry 23 red 1 4)
  (Robert (sane 1.77 58) rags ())
As simple as that. Lisp excels at manipulating lists and so has lots of functions that can do almost anything to a list. And now comes the interesting part:

Lists as function calls

Even the programs written in Lisp are themselves lists and share the same simple syntax:

  (+ 2 3)
This is a three element list but also a very simple program written in Lisp. When the Lisp interpreter evaluates a list (or in other words tries to "execute" it), it assumes that the first element of the list is the name of a function to be called and the rest of the elements are the arguments that the function needs to do useful work. So in the preceding case the function represented by the symbol "+" is called with two arguments that result from the evaluation of "2" and "3". As a number always evaluates to itself, the function "+" actually gets the numbers "2" and "3" as arguments and you hopefully get the correct result and a new prompt from the lisp interpreter:

  <cl> (+ 2 3)
  5
  <cl>
It is important to note that the arguments to a function always get evaluated before being passed to it (of course unless you specify the reverse, as we will see shortly). So what happens if one of the arguments of this simple function call is not a number, but another list?:

  (+ 2 (* 5 6))
The same simple rules apply. The Lisp interpreter just treats each list as before. It calls the function with the result of evaluating its arguments. In this case the sequence is: the function "+" gets called with "2" as the first argument and with the result of evaluating "(* 5 6)" as the second. This second evaluation proceeds by calling "*" with the result of evaluating "5" and "6" which, being numbers, evaluate to themselves. The result of this intermediate step is of course "30" (check with your calculator if in doubt!) which is the number that gets sent to our first function ("+") as its second argument:

  <cl> (+ 2 (* 5 6))
  32
  <cl> 
Summarizing, when the interpreter evaluates a list it calls the first element as a function with the result of evaluating the rest of them as arguments. In other words always the operation first followed by the arguments.

Blocking evaluation

But sometimes you don't want the arguments of a function to be evaluated! In the following example we are going to use a function called "first" that returns the first element of a list:

  (first (1 2 3))
What's wrong with this? The problem is that an interpreter is a pretty dumb software creature, always playing by the same boring rules. When it evaluates the first and only argument of the function "first", it will tries to use the number "1" as a function name called with "2" and "3" as arguments, and of course it will print a nice error message.

  <cl> (first (1 2 3))
  Error: Funcall of 1 which is a non-function.
  [1] <cl> 
Lisp has no way of knowing that "(1 2 3)" is only a list of numbers and not another function to be evaluated. So you have to tell the lisp interpreter explicitly what you want by "quoting" the argument:

  (first '(1 2 3))
The "'" character (quote) tells the Lisp interpreter that it must not evaluate the following form, but instead must use it "as-is". So now the interpreter calls the function "first" with the result of not evaluating the following list, and we get the expected result:

  <cl> (first '(1 2 3))
  1
  <cl>
- Note:
After an error occurs the interpreter stops and awaits further commands as usual, but saves the context (variable values, pending function calls, etc) where the error occurred so that you can examine what happened in more detail. The level of error nesting is signalled by a number between square brackets before the <cl> prompt. You can use the :reset command to return the error nesting level to 0 and get rid of the pending context information.

  [76]<cl> :reset
  <cl> 
You should note that Lisp does not make any distinction between what is data and what is program. A list is a list and can be either data or a program... or even both! Things get really interesting when programs start manipulating or creating lists that are really programs. That is one of the reasons why Lisp was originally invented and has been the most widely used program for artificial intelligence applications.

Macros

Of course life forms in LispLand are not always so simple. In lisp there are really three different species of animals that the interpreter recognizes and knows how to handle: functions, macros and special forms (uh oh... this is getting complicated). When the interpreter receives a list to be evaluated it looks at the first element of the list to decide what to do. If the first element is a function name everything runs according to the rules we have just seen. The rest of the elements are evaluated and then the function is called with the result of the evaluation of its arguments. But suppose the interpreter finds that the first element is really the name of a macro. Aha!, we just stumbled into the second species of LispLand beasts!

When the first argument is a macro name the rest of the list gets shipped to the macro, which then decides by itself what to do with it. The purpose of a macro call is to return to the interpreter a program (actually a list) that is then executed instead of the original macro call. You can look to a macro as a small compiler that extends the Lisp syntax on the fly in ways that can make something easier to use from the human viewpoint.

And don't forget that the macro is a small compiler that decides by itself what to do with the arguments. Usually a macro does not evaluate its arguments! If in doubt look in the appropriate documentation for the software package you are using. Macros can easily surprise an unwary user by failing to evaluate parameters.

Defining new functions

So far we have been able to use the interpreter to evaluate forms been typed in by the user. But how do we build a new program? We build programs by defining new functions and adding them to the lisp environment. We do this by using a macro called "defun" as in the following example:

  <cl> (defun plus (a b) (+ a b))
  PLUS
  <cl>
The first element of this list is of course the name of the macro we are calling. The second is the name to be given to the new function (note that the macro defun, being an independent entity from the interpreter, decides what to do with its arguments and so it does not evaluate the name!). The third element in our list is the list of arguments that the function will receive when called. This is followed by an indeterminate number of forms that comprise the body of the function definition (in our example just one). These forms get executed in sequence when a call is made to the newly defined function. So if you now say:

  <cl> (plus 4 (/ 10 3)) 
  22/3
  <cl>
you will find that the function just defined is available for use. Just to see the kind of syntactic sweetening that you get from using a macro, let's see what the Lisp interpreter really executes to create the new function:

  <cl> (macroexpand '(defun plus (a b) (+ a b)))

  (PROGN (EXCL::SET-FUNCTION 'PLUS (FUNCTION (LAMBDA (A B) (BLOCK PLUS (+ A B))))) (REMPROP 'PLUS 'EXCL::%FUN-DOCUMENTATION) 'PLUS) 
  T 
  <cl> 
Macroexpand does just half of the work of a normal macro call. It returns the program that is equivalent to the macro call so you can examine it (the process of transforming the macro call to a normal Lisp program is called macro expansion). As you see the guy who wrote the defun macro saved you from having to deal with a lot of gory details!

Using variables

Common Lisp normally uses symbols as names of variables. A variable is a binding (association) between a name and a place where something can be stored. So far we know how to access a variable. The name of a variable evaluates to the contents of the associated storage place. But how do we modify the contents of that storage place? For that purpose we will use a powerful macro called setf. Let's start with an example:

  <cl> (setf v1 '(1 2 3))
  (1 2 3)
  <cl>
What setf did was to bind the symbol "v1" to a place, where it stored the list (1 2 3). Now you can use v1 as a variable, and see that it evaluates to its contents...

  <cl> v1
  (1 2 3)
  <cl>
...just as a function evaluates to its result (note again that the evaluation of v1 is (1 2 3)):

  <cl> (first v1)
  1
  <cl>
The first argument to setf is the name of a place. In that respect setf is quite powerful because you can specify almost anything where something can be stored, and the macro call will generate the appropriate lisp code to modify the contents of that place. The second argument is the object to be stored, and there is no restriction on what it can be. Just keep in mind that setf evaluates the second argument, so you must use a quote to stop evaluation if necessary (see the preceding example).

You should note that the arguments to a function call get the same treatment. Each name declared in the function definition is bound to a temporary storage place that gets created when you call the function, and is initialized with the received value. The name can even be the same as that of another globally declared variable. In that case the value accessed by the code that is part of your definition is the locally declared value. The global variable gets shadowed by the local declaration. That is the reason why you should not think of a variable as the name. It is a place where things are stored, and the name is associated with a place depending on the context where the name appears.

Defining temporary bindings

When you are defining a new function you will probably find the need to declare variables that will only be used during the execution of the function. Lisp provides a way for a programmer to do that through the use of the "let" special form. After its name, let expects to see a list specifying the variables to be defined and optional initial values. If you specify only the name, then its initial value will be NIL. If instead of only a name you use a two element list, the first element will become the variable name and the result of evaluating the second will determine the initial value. After the list that defines the variables, let expects any number of forms that will recognize when evaluated the existence of the newly bound variables.

  (let ((x 12)(y 22) z)
    (format t "x = ~F~%" x)
    (format t "sum = ~F~%" (+ x y))
  )
In this example we define three variables: x, y and z with initial values equal to 12, 22 and NIL. The body of "let" consists of two calls to the format function, which prints some of the values. If you evaluate the following at the lisp prompt...

  (setf x 3.444)
...and then evaluate again the previous example you will realize that when the body of let is being executed there are two different places holding values with the same name. The global place that you defined by executing the setf is shadowed by the local binding that let is doing. If you examine the global x afterwards you will find it still containing 3.444.


Where to go next?

As stated in the introduction, this is just a tiny glimpse of the Common Lisp language. If you want more information you should get one of the books mentioned there. And of course you can now move on with more confidence to the task of making music using CLM or CM.


©1995..1997 Fernando Lopez-Lezcano. All Rights Reserved.
nando@ccrma.stanford.edu