A function's parameter list defines the variables that will be used to hold the arguments passed to the function when it's called.[56] If the function takes no arguments, the list is empty, written as ()
. Different flavors of parameters handle required, optional, multiple, and keyword arguments. I'll discuss the details in the next section.
If a string literal follows the parameter list, it's a documentation string that should describe the purpose of the function. When the function is defined, the documentation string will be associated with the name of the function and can later be obtained using the DOCUMENTATION
function.[57]
Finally, the body of a DEFUN
consists of any number of Lisp expressions. They will be evaluated in order when the function is called and the value of the last expression is returned as the value of the function. Or the RETURN-FROM
special operator can be used to return immediately from anywhere in a function, as I'll discuss in a moment.
In Chapter 2 we wrote a hello-world
function, which looked like this:
(defun hello-world () (format t "hello, world"))
You can now analyze the parts of this function. Its name is hello-world
, its parameter list is empty so it takes no arguments, it has no documentation string, and its body consists of one expression.
(format t "hello, world")
The following is a slightly more complex function:
(defun verbose-sum (x y)
"Sum any two numbers after printing a message."
(format t "Summing ~d and ~d.~%" x y)
(+ x y))
This function is named verbose-sum
, takes two arguments that will be bound to the parameters x
and y
, has a documentation string, and has a body consisting of two expressions. The value returned by the call to +
becomes the return value of verbose-sum
.
Function Parameter Lists
There's not a lot more to say about function names or documentation strings, and it will take a good portion of the rest of this book to describe all the things you can do in the body of a function, which leaves us with the parameter list.
The basic purpose of a parameter list is, of course, to declare the variables that will receive the arguments passed to the function. When a parameter list is a simple list of variable names—as in verbose-sum
—the parameters are called required parameters. When a function is called, it must be supplied with one argument for every required parameter. Each parameter is bound to the corresponding argument. If a function is called with too few or too many arguments, Lisp will signal an error.
However, Common Lisp's parameter lists also give you more flexible ways of mapping the arguments in a function call to the function's parameters. In addition to required parameters, a function can have optional parameters. Or a function can have a single parameter that's bound to a list containing any extra arguments. And, finally, arguments can be mapped to parameters using keywords rather than position. Thus, Common Lisp's parameter lists provide a convenient solution to several common coding problems.
Optional Parameters
While many functions, like verbose-sum
, need only required parameters, not all functions are quite so simple. Sometimes a function will have a parameter that only certain callers will care about, perhaps because there's a reasonable default value. An example is a function that creates a data structure that can grow as needed. Since the data structure can grow, it doesn't matter—from a correctness point of view—what the initial size is. But callers who have a good idea how many items they're going to put into the data structure may be able to improve performance by specifying a specific initial size. Most callers, though, would probably rather let the code that implements the data structure pick a good general-purpose value. In Common Lisp you can accommodate both kinds of callers by using an optional parameter; callers who don't care will get a reasonable default, and other callers can provide a specific value.[58]
To define a function with optional parameters, after the names of any required parameters, place the symbol &optional
followed by the names of the optional parameters. A simple example looks like this:
(defun foo (a b &optional c d) (list a b c d))
When the function is called, arguments are first bound to the required parameters. After all the required parameters have been given values, if there are any arguments left, their values are assigned to the optional parameters. If the arguments run out before the optional parameters do, the remaining optional parameters are bound to the value NIL
. Thus, the function defined previously gives the following results:
(foo 1 2) ==> (1 2 NIL NIL)
(foo 1 2 3) ==> (1 2 3 NIL)
(foo 1 2 3 4) ==> (1 2 3 4)
Lisp will still check that an appropriate number of arguments are passed to the function—in this case between two and four, inclusive—and will signal an error if the function is called with too few or too many.
Of course, you'll often want a different default value than NIL
. You can specify the default value by replacing the parameter name with a list containing a name and an expression. The expression will be evaluated only if the caller doesn't pass enough arguments to provide a value for the optional parameter. The common case is simply to provide a value as the expression.
(defun foo (a &optional (b 10)) (list a b))
This function requires one argument that will be bound to the parameter a
. The second parameter, b
, will take either the value of the second argument, if there is one, or 10.
(foo 1 2) ==> (1 2)
(foo 1) ==> (1 10)
Sometimes, however, you may need more flexibility in choosing the default value. You may want to compute a default value based on other parameters. And you can—the default-value expression can refer to parameters that occur earlier in the parameter list. If you were writing a function that returned some sort of representation of a rectangle and you wanted to make it especially convenient to make squares, you might use an argument list like this:
(defun make-rectangle (width &optional (height width)) ...)
which would cause the height
parameter to take the same value as the width
parameter unless explicitly specified.
Occasionally, it's useful to know whether the value of an optional argument was supplied by the caller or is the default value. Rather than writing code to check whether the value of the parameter is the default (which doesn't work anyway, if the caller happens to explicitly pass the default value), you can add another variable name to the parameter specifier after the default-value expression. This variable will be bound to true if the caller actually supplied an argument for this parameter and NIL
otherwise. By convention, these variables are usually named the same as the actual parameter with a "-supplied-p" on the end. For example:
(defun foo (a b &optional (c 3 c-supplied-p))
(list a b c c-supplied-p))
This gives results like this:
(foo 1 2) ==> (1 2 3 NIL)
56
Parameter lists are sometimes also called
57
For example, the following:
(documentation 'foo 'function)
returns the documentation string for the function foo
. Note, however, that documentation strings are intended for human consumption, not programmatic access. A Lisp implementation isn't
58
In languages that don't support optional parameters directly, programmers typically find ways to simulate them. One technique is to use distinguished "no-value" values that the caller can pass to indicate they want the default value of a given parameter. In C, for example, it's common to use NULL
as such a distinguished value. However, such a protocol between the function and its callers is ad hoc—in some functions or for some arguments NULL
may be the distinguished value while in other functions or for other arguments the magic value may be -1 or some #defined
constant.