FUNCALL
is the one to use when you know the number of arguments you're going to pass to the function at the time you write the code. The first argument to FUNCALL
is the function object to be invoked, and the rest of the arguments are passed onto that function. Thus, the following two expressions are equivalent:
(foo 1 2 3) === (funcall #'foo 1 2 3)
However, there's little point in using FUNCALL
to call a function whose name you know when you write the code. In fact, the previous two expressions will quite likely compile to exactly the same machine instructions.
The following function demonstrates a more apt use of FUNCALL
. It accepts a function object as an argument and plots a simple ASCII-art histogram of the values returned by the argument function when it's invoked on the values from min
to max
, stepping by step
.
(defun plot (fn min max step)
(loop for i from min to max by step do
(loop repeat (funcall fn i) do (format t "*"))
(format t "~%")))
The FUNCALL
expression computes the value of the function for each value of i
. The inner LOOP
uses that computed value to determine how many times to print an asterisk to standard output.
Note that you don't use FUNCTION
or #'
to get the function value of fn
; you want it to be interpreted as a variable because it's the variable's value that will be the function object. You can call plot
with any function that takes a single numeric argument, such as the built-in function EXP
that returns the value of e raised to the power of its argument.
CL-USER> (plot #'exp 0 4 1/2)
*
*
**
****
*******
************
********************
*********************************
******************************************************
NIL
FUNCALL
, however, doesn't do you any good when the argument list is known only at runtime. For instance, to stick with the plot
function for another moment, suppose you've obtained a list containing a function object, a minimum and maximum value, and a step value. In other words, the list contains the values you want to pass as arguments to plot
. Suppose this list is in the variable plot-data
. You could invoke plot
on the values in that list like this:
(plot (first plot-data) (second plot-data) (third plot-data) (fourth plot-data))
This works fine, but it's pretty annoying to have to explicitly unpack the arguments just so you can pass them to plot
.
That's where APPLY
comes in. Like FUNCALL
, the first argument to APPLY
is a function object. But after the function object, instead of individual arguments, it expects a list. It then applies the function to the values in the list. This allows you to write the following instead:
(apply #'plot plot-data)
As a further convenience, APPLY
can also accept "loose" arguments as long as the last argument is a list. Thus, if plot-data
contained just the min, max, and step values, you could still use APPLY
like this to plot the EXP
function over that range:
(apply #'plot #'exp plot-data)
APPLY
doesn't care about whether the function being applied takes &optional
, &rest
, or &key
arguments—the argument list produced by combining any loose arguments with the final list must be a legal argument list for the function with enough arguments for all the required parameters and only appropriate keyword parameters.
Anonymous Functions
Once you start writing, or even simply using, functions that accept other functions as arguments, you're bound to discover that sometimes it's annoying to have to define and name a whole separate function that's used in only one place, especially when you never call it by name.
When it seems like overkill to define a new function with DEFUN
, you can create an "anonymous" function using a LAMBDA
expression. As discussed in Chapter 3, a LAMBDA
expression looks like this:
(lambda (parameters) body)
One way to think of LAMBDA
expressions is as a special kind of function name where the name itself directly describes what the function does. This explains why you can use a LAMBDA
expression in the place of a function name with #'
.
(funcall #'(lambda (x y) (+ x y)) 2 3) ==> 5
You can even use a LAMBDA
expression as the "name" of a function in a function call expression. If you wanted, you could write the previous FUNCALL
expression more concisely.
((lambda (x y) (+ x y)) 2 3) ==> 5
But this is almost never done; it's merely worth noting that it's legal in order to emphasize that LAMBDA
expressions can be used anywhere a normal function name can be.[66]
Anonymous functions can be useful when you need to pass a function as an argument to another function and the function you need to pass is simple enough to express inline. For instance, suppose you wanted to plot the function 2x. You could define the following function:
(defun double (x) (* 2 x))
which you could then pass to plot
.
CL-USER> (plot #'double 0 10 1)
**
****
******
********
**********
************
**************
****************
******************
********************
NIL
But it's easier, and arguably clearer, to write this:
CL-USER> (plot #'(lambda (x) (* 2 x)) 0 10 1)
**
****
******
********
**********
************
**************
****************
******************
********************
NIL
The other important use of LAMBDA expressions is in making closures, functions that capture part of the environment where they're created. You used closures a bit in Chapter 3, but the details of how closures work and what they're used for is really more about how variables work than functions, so I'll save that discussion for the next chapter.
6. Variables
The next basic building block we need to look at are variables. Common Lisp supports two kinds of variables: lexical and dynamic.[67] These two types correspond roughly to "local" and "global" variables in other languages. However, the correspondence is only approximate. On one hand, some languages' "local" variables are in fact much like Common Lisp's dynamic variables.[68] And on the other, some languages' local variables are lexically scoped without providing all the capabilities provided by Common Lisp's lexical variables. In particular, not all languages that provide lexically scoped variables support closures.
66
In Common Lisp it's also possible to use a LAMBDA
expression as an argument to FUNCALL
(or some other function that takes a function argument such as SORT
or MAPCAR
) with no #'
before it, like this:
(funcall (lambda (x y) (+ x y)) 2 3)
This is legal and is equivalent to the version with the #'
but for a tricky reason. Historically LAMBDA
expressions by themselves weren't expressions that could be evaluated. That is LAMBDA
wasn't the name of a function, macro, or special operator. Rather, a list starting with the symbol LAMBDA
was a special syntactic construct that Lisp recognized as a kind of function name.
But if that were still true, then (funcall (lambda (...) ...))
would be illegal because FUNCALL
is a function and the normal evaluation rule for a function call would require that the LAMBDA
expression be evaluated. However, late in the ANSI standardization process, in order to make it possible to implement ISLISP, another Lisp dialect being standardized at the same time, strictly as a user-level compatibility layer on top of Common Lisp, a LAMBDA
macro was defined that expands into a call to FUNCTION
wrapped around the LAMBDA
expression. In other words, the following LAMBDA
expression:
(lambda () 42)
exands into the following when it occurs in a context where it evaluated:
(function (lambda () 42)) ; or #'(lambda () 42)
This makes its use in a value position, such as an argument to FUNCALL, legal. In other words, it's pure syntactic sugar. Most folks either always use #' before LAMBDA expressions in value positions or never do. In this book, I always use #'.
67
Dynamic variables are also sometimes called
68
Early Lisps tended to use dynamic variables for local variables, at least when interpreted. Elisp, the Lisp dialect used in Emacs, is a bit of a throwback in this respect, continuing to support only dynamic variables. Other languages have recapitulated this transition from dynamic to lexical variables—Perl's local
variables, for instance, are dynamic while its my
variables, introduced in Perl 5, are lexical. Python never had true dynamic variables but only introduced true lexical scoping in version 2.2. (Python's lexical variables are still somewhat limited compared to Lisp's because of the conflation of assignment and binding in the language's syntax.)