I’ve written a few posts about features of the Lisp language that a newcomer arriving from C++ might not have encountered. I’m going through them alphabetically, so here is a summary page of the functions beginning with the letters ‘E’ and ‘F’:
Category Archives: Uncategorized
The less-familiar parts of Lisp for beginners — function-lambda-expression
Now we move on to function-lambda-expression. While programmers might occasionally find a reason to want to print out a function’s source code from within the running program, be warned that this function is not guaranteed to produce helpful results. While the implementation is encouraged to produce the lambda expression for the function, at least on non-compiled forms, it is permitted to return nil for any input. Consequently, you cannot rely on this function in portable code, and should only use it in non-essential contexts after verifying that it does something interesting on the platform it’s running on at the time.
The less-familiar parts of Lisp for beginners — function-keywords
Next on the tour of uncommon Lisp features is function-keywords. I can’t really think of a time when a programmer might need to use this, it is a function that one typically would use in the implementation of the generic function framework in Common Lisp, as part of the system to determine whether a particular method matches a particular generic function definition.
If any reader can find a context, outside of implementing Lisp, when the programmer might need to use this, please let me know in the comments.
The less-familiar parts of Lisp for beginners — function
Continuing our alphabetical tour through Lisp features that the newcomer might not have previously encountered, we arrive at function.
There are two common use cases for this special operator. One is to interpret a function name in the current lexical environment and return the appropriate function object. The other is to build lexical closures.
The first case was discussed some in the article on fboundp. Recall that a function name defined by labels is not visible to called functions. So, passing the name of a function defined with labels into, say, the predicate of the sort function will fail because sort will try to look up the function name in the set of interned symbols, and labels does not influence the set of interned symbols. The function special operator allows the programmer to resolve the name in the lexical environment and use it to pass not the name of the function, but the actual function object itself. Here is an example, first with incorrect syntax that raises an error:
(defun demonstrate-function () (let ((input-list (list 10 2 4 9 5))) (labels ((even-odd-sort (v1 v2) "Sorts the values, but even numbers are always less than odd." (cond ((evenp v1) (or (oddp v2) (< v1 v2))) (t (and (oddp v2) (< v1 v2)))))) (format t "Before sorting: ~A~%" input-list) (format t "After sorting: ~A~%" (sort input-list 'even-odd-sort)))))
Running produces this:
CL-USER> (demonstrate-function) Before sorting: (10 2 4 9 5) ; Evaluation aborted on #<UNDEFINED-FUNCTION EVEN-ODD-SORT {100544DAC3}>.
The reason this failed is that labels did not insert even-odd-sort into the set of interned symbols. When the sort function received the symbol ‘even-odd-sort, it had no way to look up the function attached to that symbol. In fact, if labels had been used to shadow an existing defun called even-odd-sort, then sort would have used the defun one, as that symbol would be interned.
The solution is to pass to sort the actual function code itself, rather than passing a name and asking sort to look it up. This is done with the function special operator:
(defun demonstrate-function () (let ((input-list (list 10 2 4 9 5))) (labels ((even-odd-sort (v1 v2) "Sorts the values, but even numbers are always less than odd." (cond ((evenp v1) (or (oddp v2) (< v1 v2))) (t (and (oddp v2) (< v1 v2)))))) (format t "Before sorting: ~A~%" input-list) (format t "After sorting: ~A~%" (sort input-list (function even-odd-sort))))))
Producing this output:
CL-USER> (demonstrate-function) Before sorting: (10 2 4 9 5) After sorting: (2 4 10 5 9) NIL
Because the passing of function objects is a fairly common operation, there is a Lisp reader shorthand for this. The code (function name) can be abbreviated #’name.
The second case, building a function closure, is demonstrated in the CLHS entry for function. Here is it in use:
CL-USER> (defun adder (x) (function (lambda (y) (+ x y)))) ADDER CL-USER> (let ((my-adder-10 (adder 10))) (format t "Calling my-adder-10 on the number 3: ~D" (funcall my-adder-10 3))) Calling my-adder-10 on the number 3: 13 NIL
This is, perhaps, a good place to talk a bit about closures. The function adder returns a closure. While the beginner might have run into that term, there is sometimes a tendency to think of a closure as a “function object”, but that entirely misses the meaning. A closure is actually the lexical environment, the variables, that are available for the function(s) within it. In this case, the closure retains the variable ‘x’. Not the value of that variable, but the variable itself. Functions contained within the closure can modify the variable, and those modifications are retained. That is, the adder function above did not return a function that adds 10, it returned a function that adds ‘x’, and the closure holds the value of ‘x’, which happens to be 10 here.
One more bit about closures. I once wrote some code which contained an error. It was for starting a number of threads, and giving each of them its own index. This was for doing some parallel matrix calculations, so different threads had to know which part of the matrix they were responsible for solving. The code to start it up seemed simple enough, and I excerpt some of it here:
(defun spawn-thread (fcn &rest args) #+(and sbcl sb-thread) (sb-thread:make-thread #'(lambda () (apply fcn args))) #+(and sbcl (not sb-thread)) (error "Cannot call 'spawn-thread' because the environment does not support threads.") #-(or sbcl) (error "We need an implementation of thread starting for this platform")) (defun start-workers () (dotimes (j *number-of-worker-threads*) (push (threads:spawn-thread 'threaded-solver frame j *number-of-worker-threads*) *thread-list*)))
This didn’t work. Sometimes several threads all worked on the same block of the matrix, leaving parts of it unsolved. They weren’t getting unique values of ‘j’. The reason for this is that spawning the worker operates in two steps. First, a new thread is spawned, and then the arguments are passed to the ‘threaded-solver function. There is a race condition here, once the thread was spawned, it was waiting around to pass the value of ‘j’ to the worker function, but meanwhile the parent thread was still running, and it was eager to increment ‘j’. The solution to this is to capture ‘j’ in a closure:
(defun start-workers () (dotimes (j *number-of-worker-threads*) (let ((closure-j j)) (push (threads:spawn-thread 'threaded-solver frame closure-j *number-of-worker-threads*) *thread-list*))))
With this change, the race condition disappeared.
The less-familiar parts of Lisp for beginners — ftype
Continuing the discussion of Lisp features not necessarily encountered by a brief introduction to the language, we come to ftype. This is a declaration, used within a declare, declaim, or proclaim, to provide a hint to the compiler as to what the inputs and outputs are of a particular function. Because Lisp is not strongly typed, it often has to do run-time type checking on operations, and by declaring the types to the compiler, the programmer allows the compiler to avoid those run-time checks, and so can produce better optimized code.
The CLHS provides these examples:
(declare (ftype (function (integer list) t) ith)
(ftype (function (number) float) sine cosine))
The first line declares that the function ith takes two arguments, the first being an integer, the second being a list. Its output is declared to be t, which matches any legal Lisp type (i.e. nothing is said about the output type).
The second line declares that the sine and cosine functions each take a numeric argument and return a float. Note that sine and cosine are not the names of those trigonometric functions in the Lisp standard, these will be user-defined functions of non-complex arguments. The types of the standard sin and cos functions are a bit more complicated, as they account for single and double precision real or complex return values.