Tag Archives: obscure commands

The less-familiar parts of Lisp for beginners — peek-char

Onward through the list of less-commonly used Lisp features.  The peek-char function has uses similar to ungetc() in the C standard.  The Lisp peek-char function returns the next character from the input stream, if any, but does not advance the file pointer, so a subsequent read will still pick up that character.  This is most commonly used in selecting dispatching functions in a token parser.

Note that the C function is allowed to push back a character that was not read, in effect modifying the input stream as seen by the file handle, while the Lisp function does not provide a way to modify the stream.

The less-familiar parts of Lisp for beginners — pairlis

We’ve skipped over a fair number of functions that, while maybe not commonly used, are fairly simple in their use and descriptions.  We’ll discuss pairlis a bit, simply because association lists are useful, but their manipulations aren’t always completely covered.  You’ll recall I began this alphabetical series of Lisp features with acons, and now we’ll talk about pairlis.

With acons, the programmer can add a new key/value pair to an association list.  The pairlis function extends this to a list of keys and a list of corresponding values.

In the acons example, I built up an association list as follows:
 

CL-USER> (let (alist)
           (setf alist (acons 1 "ONE" alist))
           (setf alist (acons 2 "TWO" alist))
           (setf alist (acons 3 "THREE" alist))
           alist)
((3 . "THREE") (2 . "TWO") (1 . "ONE"))

Here’s how you can do this more simply with pairlis:
 
CL-USER> (let ((alist (pairlis '(1 2 3) '("ONE" "TWO" "THREE"))))
           alist)
((3 . "THREE") (2 . "TWO") (1 . "ONE"))

Remember this function when you want to build multiple association list entries in code.

The less-familiar parts of Lisp for beginners — notinline

Continuing the discussion of less familiar parts of Lisp for newcomers from C++, we’ll discuss the notinline declaration a bit.  This pairs up with inline, of course, but we’ve saved this discussion until now because of the context I wanted to introduce first, particularly as relates to symbols and the manipulation of their function cells.

By now, you’ve noticed that Lisp function calls involve execution-time symbol resolution.  While you’re probably familiar with load-time linking in C++ programs, things are a bit different in Lisp, because it is legal to change the function definition associated with a symbol, even while a program is running.  Perhaps, then, you’ve wondered about inline functions in Lisp.  How can you inline a function when the very definition of the function might change from one call to the next?

In C++, the compiler has fairly broad discretion on inlining functions.  The programmer may also hint that inlining is desirable for a particular function, with the inline keyword.  Some compilers recognize options that ask them to inline any function that is “reasonable” to inline, subject to the implementor’s definition.

In Lisp, the inline declaration tells the system that it is allowed and encouraged to inline this function, until cancelled by a notinline declaration.  Note that the Lisp implementation is free to ignore this, and is not required to have the ability to inline.  If a function attached to an interned symbol is inlined, and the function definition is later changed, those areas where the old form was inlined are not changed.  This can lead to confusion in an active development context, or when manipulations of the symbol table are made, such as a package import that shadows the function in question.  One way to avoid this confusion is to try to confine your use of inline to non-interned functions such as those created with labels or flet.

Here, then, is a transcript showing the effect of inlining:
 

CL-USER> (declaim (inline function-1))
; No value
CL-USER> (defun function-1 ()
           (format t "This is the old function-1~%"))
FUNCTION-1
CL-USER> (defun function-2 ()
           (format t "In function-2, calling by symbol:~%~T")
           (funcall 'function-1)
           (format t "In function-2, calling by function:~%~T")
           (funcall #'function-1))
FUNCTION-2
CL-USER> (declaim (notinline function-1))
; No value
CL-USER> (function-2)
In function-2, calling by symbol:
 This is the old function-1
In function-2, calling by function:
 This is the old function-1
NIL
CL-USER> (setf (fdefinition 'function-1)
               (lambda () 
                 (format t "This is the new function-1~%")))
#<FUNCTION (LAMBDA ()) {10036FEE7B}>
CL-USER> (function-2)
In function-2, calling by symbol:
 This is the new function-1
In function-2, calling by function:
 This is the old function-1
NIL

You can see that function-1 was declared inlineable, and then we defined function-2, which invokes function-1.  I invoke function-2, and nothing unexpected happens.  Then, I change the function bound to the function-1 symbol (see the earlier article on fdefinition if this is unclear).  Now, when I invoke function-2, I get both the old and new forms of function-1 appearing.  Why is that?  If you recall our discussion at fboundp, the single-quote prefix tells funcall, at run-time, to look up the function bound to the symbol.  On the other hand, the #’ prefix tells the reader to replace the content with the function expansion of the symbol.  Since the function is inlineable at the time the reader sees this form, the definition of function-1 is inserted into the code at that point, and persists even if that definition is later modified or deleted.  So, this is confusing, try not to do it.

The less-familiar parts of Lisp for beginners — notany

Onward through our list of less familiar parts of common Lisp.  I had originally skipped every, but I think I’d like to bring it up, along with its related functions, just to point out something helpful.  The functions every, some, notevery, and notany may look redundant in the context of Lisp’s and and or, but they do have some uses by virtue of the fact that they are functions.

Macros are useful, but there are some places they cannot go.  You cannot pass a macro to mapcar, or funcall, or apply.  While that is inconvenient, the and and or macros of Lisp have the familiar short-circuiting behaviour of && and || in C++.  The notany function and its relatives are functions, and so do not short-circuit, all of their arguments are evaluated whether or not there is an intermediate result that conclusively establishes the return code.

The reason I bring this up is that newcomers might not have noticed the way these functions can be used in mapcar contexts and the like, to achieve the effect of determining, for instance, whether a list of forms are all true.  Let’s look at the and case:
 

CL-USER> (let ((num 10))
           (funcall 'and (numberp num) (> num 9)))
; Evaluation aborted on #<UNDEFINED-FUNCTION AND {1003E5B0F3}>.
CL-USER> (let ((num 10))
           (funcall 'every 'identity (list (numberp num) (> num 9))))
T

As you see, the funcall failed because and, being a macro, cannot be invoked in this manner.

Note that this example is only an example.  The particular way I’ve laid this out is for illustrative purposes, and if we were really intent on making this particular test, we would use ‘and as follows:
 

CL-USER> (let ((num 10))
           (and (numberp num)
                (> num 9)))
T

In fact, the example I give above with funcall and every would be wrong in real code, because both the numberp and the > operations would be evaluated up front, and if the variable num did not hold a number, then the comparison operator would error out.  This is only to show, in a simple context, how notany and its relatives, being functions, might be used in places where macros are illegal.

The less-familiar parts of Lisp for beginners — next-method-p

The next-method-p local function must only be called from within a method definition.  It allows a method to determine whether there is a next method that might be available to call-next-method.  This is helpful, as invoking call-next-method without a next method available will invoke the no-next-method generic function which, unless overridden by a user-supplied method, signals an error.