5. PROGRAM STRUCTURE
This chapter contains the following sections:
5.1 Program Structure: Forms
5.2 Program Structure: Functions
5.3 Program Structure: Top-Level Forms
One fundamental premise of LISP is that LISP programs have the same structure as LISP data objects.
LISP programs consist of forms and functions.
Forms are evaluated (in some context) and produce values and side effects.
Functions are invoked by being applied to arguments. A function evaluates forms in order to perform a computation; the most important kind of form is one that executes a function call.
5.1 Program Structure: Forms
a form is a data object which is evaluated as a program to produce zero or more values, which are in turn data objects. A meaningful form is one which can evaluated. Note that all Common LISP objects can be evaluated, if only to themselves. This section contains the following subsections, which discuss various kinds of forms:
5.1.1. Program Structure: Self-Evaluating Forms
5.1.2. Program Structure: Variables
5.1.3. Program Structure: Special Forms
5.1.4. Program Structure: Macros
5.1.5 Program Structure: Function Calls
Meaningful forms fall into the following categories:
· symbols, which are used as variables;
· self-evaluating forms (which includes all other data objects).
We are primarily interested in lists in this chapter. Lists, considered as meaningful forms, may be divided into three categories:
· function calls,
· special forms and
· macro calls.
We will discuss self-evaluating forms and symbols first.
5.1.1. Program Structure: Self-Evaluating Forms
Examples of self-evaluating forms include fixnums, true integers, bignums, characters, hash tables, strings, vectors, generalized arrays, and bit-vectors.
When such an object is evaluated, that object is returned as the value of the form. In the case of fixnums or characters, what is actually returned is a copy of the original, which is stored directly in the virtual address; this implementation detail is transparent to the user.
The symbols nil and t are also self-evaluating forms. nil when written () (the empty list) likewise evaluates to itself.
The importance of this (beginners take note) is that none of these forms need to take a quote when used as data. Therefore,: only lists and non-keyword symbols (except for nil and t) need to be quoted; for instance:
(setq
x 15 ; an integer, no quote needed
y '(a b c) ; a list, needs a quote
; (otherwise 'a' will be used as a;function)
z "hello, world" ; a string, no quote needed
a 'z) ; non-keyword symbol, needs a;quote
Quoting self-evaluating forms is not harmful but meaningless.
Keywords (symbols written with a leading colon) also evaluate to themselves: the value of :end is :end. This can be useful when using a symbol only as a name for something (rather than for its value), eliminating the need to quote the symbol. For instance:
(case (sym)
(:test (print "this is only a test"))
(:test-not (print "this is not a test"))
(t (print "something else")))
5.1.2. Program Structure: Variables
Variables are named by symbols in Common LISP. The result of evaluating a symbol as a form is the value of the variable it names.
For instance, after the setq form in the previous section is run, the value of the variable x will be 3.
Variables can be assigned a value, using (e.g.) setq, or given a binding, using (e.g.) let.
When a construct binds a value to a variable, it saves the old value before the new value is bound; on exit from the construct the previous value is restored.
As discussed in Scope and Extent, there are actually two kinds of variables in Common LISP:
· lexical variables (or static)
· dynamic variables (also known as special).
A lexically bound variable make its value accessible to forms occurring at any place within the program construct that creates its variable binding. This is a spatial limitation (without a temporal limitation, since the binding can be accessed, in theory, as long as any possibility of reference remains, specifically in a closure).
A special or dynamically bound variable makes its value accessible to any form, irregardless of its location in the program as long as its binding is in effect. Thus there is a temporal, but not a spatial limitation.
Special variables can have a global value, which is effected by assignment rather than binding (e.g. by setq). A binding is by definition not global.
If a special variable has no value it is said to be unbound. This term is misleading; such a variable is actually without an assigned value, rather than without a binding.
By default, all global variables are unbound unless and until they are explicitly assigned a value. This is with the exception of global variables defined by Common LISP or by the implementation to have values when the LISP system is booted.
The function makeunbound can be used to make a variable unbound. It is an error to refer to an unbound variable.
Certain global variables are called "named constants".
These variables have a global value and may not be bound or assigned to.
There are several kinds of named constants.
The symbols nil and t belong to this category. The global value of nil is always nil, and the global value of t is always t. A new value cannot be assigned to, nor a binding established for nil or t.
The defconstant macro can be used to set up programmer defined constants which cannot be assigned to or bound. Only defconstant can be used to refdefine such a constant.
Keywords may not be assigned to or bound; a keyword always evaluates to itself.
5.1.3. Program Structure: Special Forms
When a list is evaluated, the element at the head of the list is used to determine what its functional value will be.
If the first element is one of the following symbols, then the list is called a special form. Note that 'special' as used here has nothing to do with special declarations.
block
catch
compiler-let
eval-when
declare
function
go
let
let*
if
multiple-value-call
multiple-value-prog1
progn
quote
return-from
setq
tagbody
the
throw
unwind-protect
labels
macrolet
progv
ANSI Common LISP removed compiler-let from the language definition and added several new special forms which this version of Star Sapphire does not support.
Special forms are generally control constructs or environmental controls with idiosyncratic syntax.
The evaluation of a special form normally produces a value or values. The evaluation of the forms return-from, go and throw may also effect a non-local exit.
The list of special forms is fixed in Common LISP. No way is provided for the programmer to create new special forms, other than from the implementation level. However, the programmer can create new syntactic constructs using defmacro.
5.1.4. Program Structure: Macros
Certain symbols can be defined to be the name of a macro using the defmacro facility. When such a name is encountered at the head of a list, a transformation is performed on the list prior to any evalution.
This facility is roughly equivalent to macros in other programming languages; however, it is much more general. LISP macros enable the programmer to truly extend the language.
This transformation is called a macro-expansion since it normally produces a more complicated form. After the expansion is performed on the form is evaluated in place of the original form and the value of the new form returned.
One advantage of macros is the flexible syntax that they allow, due to the destructuring facility. This allows definition of parameter lists which are analyzed recursively, as opposed to defun parameter lists which are 'flat'.
Macros are often used in conjunction with the backquote syntax, which allows writing readable code to generate complicated list structure.
One key feature of macros which beginners should realize is that list or symbol arguments to macroexpansions do not necessarily require quoting, since they can be expanded into code which is quoted. Effectively, a macro per se does not evaluate its arguments; this is up to the expansion.
Following is a simple example of a defmacro, and how it can differ from a defun when it is invoked. Note the ->, which indicates a macroexpansion.
; the macro 'penult' expands into code which gives the
; next to last element of x, where x can be a
; generalized sequence.
(defmacro penult (x) `(elt ',x (- (length ',x) 2)))
; subsequently
(penult (a b c)) ; note that the list is not quoted
->
; expansion of this macro produces (roughly):
(let ((x '(a b c))) ; quote was generated automatically
(elt x (- (length x) 2)))
=> ; which when evaluated produces:
B ; next to last symbol in list
; likewise
(penult "12345")
->
; expansion of this macro produces (roughly):
(let ((x '"12345")) ; note that the automaticallygenerated
; quote is not really needed here but is ;harmless
(elt x (- (length x) 2)))
=> ; which when evaluated produces
#\4 ; next to last character in "12345"
; however, if you were under the misapprehension that ;penult is a defun you might quote a list argument, which ends ;up doing something unexpected:
(penult '(a b c))
->
; expansion of this macro produces (roughly):
(let ((x '(quote (a b c)))) ; not what you ;wanted
(elt x (- (length x) 2)))
=> ; which when evaluated produces:
QUOTE
This last example is (probably inadvertantly) asked to return the next to last element of the list (quote (a b c)), that is, the symbol quote.
If you are not sure what your macros are expanding into, you can toggle macroexpansion tracing using the Star Sapphire specific mtrace function.
Why use macros? Typically macros are used to 1) enhance performance and 2) extend the language in useful ways. However, debugging code with a lot of macros can be frustrating and ultimately self-defeating. There needs to be a real justification to use this language feature, not just writing clever code for its own sake.
As an example of the first criteria, under most circumstances calling a function can have a slightly greater overhead than running some code which has been expanded 'inline'. Writing penult as a macro in a real world program would make sense if you needed to get the next to the last element of a sequence thousands of times in a tight loop in some time-critical code.
Another example of the first criteria is that, in Star Sapphire, macros compiled into fastload files load very quickly, much more so than defuns compiled into fastload files.
The second reason to use macros is to make new syntactic constructs; this should only be done if using a macro will actually improve the readability of the code. In this case, it is hard to see how writing penult as a defmacro rather than as a defun would improve the readability of a program.
5.1.4.1 Caveat about macros
With the power of defmacro comes a drawback in terms of portability. This is mostly due to the sketchy way that Common LISP macros are specified in the language definition. For instance, a C programmer can assume that a given macro will produce the same expansion across all C compilers; a Common LISP programmer can only assume that a macro will have the same effect in other Common LISP implementations. In practice, however, it is always possible that a Common LISP macro will have a different effect in other implementations.
This is for two primary reasons:
As stated above, the expansion generated by macros is not specified by Common LISP. In Star Sapphire LISP macros get transformed into let forms with appropriate variable lists, which seems to be a common implementation strategy. There are many ways to do this, however.
Nor does the specification state clearly exactly when macros are to be expanded. Star Sapphire expands all macros as a separate pass which occurs after a form is read at the top level; this is prior to the form being handed off to the incremental compiler and then evaluator. Although this makes sense, there can be other implementation strategies. In previous versions of Star Sapphire, the macro expansion occured as part of the incremental compiler; this was found to produce different effects than other implementations and was altered.
5.1.4.2 Standard Macros
There are a number of standard macros in Common LISP in addition to those that the user can define using defmacro. For example, and, or, prog and dotimes. These are also called built-in macros in Star Sapphire documentation.
In Star Sapphire, these are implemented using a mechanism identical to special forms which is only available at the implementation level. Sometimes standard macros are expanded like normal macros, albeit into specialized code, such as prog and prog*. More usually standard macros are evaluated directly, such as and, or dolist; this is for efficiency. Following is a list of all Common LISP forms implemented in this fashion, including those which are considered special forms (preceeded by an asterisk):
and
*
blockcase
*
catch*
compiler-letcond
*
declaredefclass
defconstant
defgeneric
defmacro
defmethod
defparameter
defun
defvar
do
do*
dolist
dotimes
*
eval-when*
function*
go*
if*
let*
let*loop
multiple-value-list
multiple-value-bind
*
multiple-value-call*
multiple-value-prog1multiple-value-setq
or
prog
prog*
prog1
prog2
*
prognpsetq
*
quotereturn
*
return-from*
setq*
tagbody*
the*
throwtypecase
unless
*
unwind-protectwhen
5.1.5 Program Structure: Function Calls
If a list is being evaluated as a form and the element at the head of the list is not a symbol that names a special form or a macro, then the form is a function call.
The first element in the list can be a symbol or a lambda expression.
All remaining elements of the list if any are treated as forms to be evaluated. Before the form is evaluated, each of these subforms are evaluated in sequence. The value obtained from each subform (or the first value if a given form returns multiple values) is used in place of that subform as an argument to the function.
The function is said to be applied to its arguments.
The functional computation normally produces a value, but it may call for a non-local exit via throw. A function that does return may produce zero or more values; see values.
If and when the function returns, whatever values it returns become the values of the function-call form.
For instance, consider the following simple arithmetic calculation:
(* 2 (+ 3 4))
First the subforms of the list beginning with * are evaluated.
The form 2 evaluates to 2.
The subform (+ 3 4) evaluates to 7 by first evaluating its arguments 3 and 4 (producing 3 and 4 respectively) and then applying them to the + function.
The result of applying the multiplication function to 2 and 7 is 14, thus the form will return 14.
5.1.5.1 Order of evaluation
In Star Sapphire functional arguments can be depended on to be evaluated from left to right; the function to be called is determined before all arguments are evaluated. The order of evaluation of Star Sapphire is as specified by ANSI Common LISP. However, the order of evaluation is not specified by Common LISP, The Language (1st ed.). ANSI Common LISP states that any program which relies on the function to be called being determined before or after the argument evaluation is in error, i.e. this can vary from implementation to implementation.
5.2 Program Structure: Functions
The first element in a list with a functional value can either be a symbol or a lambda expression. A lambda expression is a list at the car of a list of arguments which defines an anonynmous functional object to be applied to the arguments in the rest of the list.
For instance:
((lambda (x)
(* x x))
5) => 25
See the entry for lambda. The constant lambda-list-keywords is a list of all lambda-list keywords defined in the implementation. The constant lambda-parameters-limit is the limit on the number of lambda list parameters.
5.3 Program Structure: Top-Level Forms
The read-eval-print loop is the standard way for the user to interact with a Common LISP implementation.
The system repeatedly reads a form from some source (this could be a file or the keyboard). The system then evaluates each form and then prints the value or values produced to the output (which can also be a file or the screen).
Any form, that is, any data object which can be evaluated, is acceptable as input to the read-eval-print loop. Some forms are particularly useful when used at the top level, that is, not embedded in some other form.
These top-level forms, as they are sometimes called, are used to
· define functions (defun)
· define macros (defmacro)
· define global values for special variables(defvar,defparameter)
· define constants (defconstant)
· specify the time of evaluation of a piece of code (eval-when)
The defun macro is the usual means of defining named functions.
The defmacro macro is used to define macros.
The defvar and defparameter macros are the usual means of specifying globally defined variables. The defconstant macro is used for defining named constants.
The eval-when special form specifies that a form or forms is to be executed only at compile time, only at load time, or when interpreted but not compiled.
In Star Sapphire LISP it is not illegal to use these forms at other than top level. However, it is traditional to restrict their use to this context.
For instance, in Star Sapphire it is possible to write several varieties of a defun inside branches of a cond, allowing the global redefinition of a function to switch based on some condition.
The Common LISP specification states, however, that not all implementations, particularly compiled implementations, are required to support this behavior. Truly portable code will use these forms only at the top level.