Category Archives: Uncategorized

The less-familiar parts of Lisp for beginners — print

As we continue through the less familiar parts of Lisp, next is the print function, and its relatives.  This isn’t a trivial topic, so we’ll discuss it a bit.  As I’m writing these articles at the newcomer to Lisp arriving from C++, let’s talk a bit about the C++ side of things.

In C, you had printf and write.  The printf class of functions produces human-readable output in a printable representation, ASCII in the old days, but potentially wchar_t strings from extended character sets.  With write, you could send values to disc in a compact binary format, not suitable for human reading, and not generally portable across platforms and implementations.  The printf function could only act on language primitives, such as integers, floats, or pointers, and reading back with the scanf class of functions was brittle and computationally intensive.  The write function could send entire structures or arrays to disc and re-read them, as long as you were very careful about the portability issues.

The C++ language, for reasons of parser compatibility, repurposed the bit-shift operators << and >> for input and output from streams.  Now, it became possible to write customized output routines for classes and structures, and input routines that could re-read these from disc.

In Lisp, you can think of two different modes of output to character devices.  There’s a human-readable format, and a machine-readable format.  When we talk about writing something out “readably”, we mean in a machine-readable format, and more specifically, in a form that the Lisp reader, using the standard readtable, can parse to produce an object equivalent, by some definition, to the object that was printed.

You’ll note that in my examples I generally use the format function with the ~A control string.  This instructs format to print the object presented, whatever it may be, for human reading (i.e. not “readably”).  The equivalent form for readable output is the ~S control string.

So, what about Lisp print, and its relatives: write, prin1, pprint, and princ?  Well, write is the low-level function that does the appropriate work.  Its behaviour is controlled by a collection of keyword arguments and special variables (dynamically-scoped variables), but generally the programmer will be calling higher-level functions that will, themselves, invoke write.

We may not have discussed dynamically-scoped variables in much detail yet, so I’ll talk about that a bit here.  A dynamically-scoped variable in Lisp looks quite unlike anything in C++.  In its declaration, it looks a bit like a global variable, but is, in fact, very different.  When a dynamically-scoped variable is reassigned in a binding, such as a let form, it appears as if it is pushing a new value onto a stack of values bound to the variable.  Within the binding, in that thread of execution, the special variable has the most recently pushed value, and when flow exits the binding, the former value is restored.  Here is an example:
 

(defparameter *my-special* 10)

(defun demonstrate ()
  (labels
      ((print-my-special ()
         (format t "*my-special* is ~A~%" *my-special*)))
    (format t "On entry to demonstrate:~%")
    (print-my-special)
    (let ((*my-special* 20))
      (format t "Inside the let:~%")
      (print-my-special)
      (setf *my-special* 30)
      (format t "After the setf:~%")
      (print-my-special))
    (format t "After the close of the let:~%")
    (print-my-special)))

with output:
 
CL-USER> (demonstrate)
On entry to demonstrate:
*my-special* is 10
Inside the let:
*my-special* is 20
After the setf:
*my-special* is 30
After the close of the let:
*my-special* is 10
NIL

So, with the use of special variables, it is possible for a Lisp thread of execution to alter certain parameters for printing without worrying about interfering with the behaviour of printing in other threads, and without having to worry about saving the former settings and restoring them on exit.

The print family of functions modify some of these special variables and call write.  So, how do these functions differ?

First, prin1.  It sets *print-escape* to non-nil, which means that the output is to be readable by the Lisp reader.

Next, princ, which sets *print-escape* and *print-readably* to nil.  The *print-readably* variable implements a superset of *print-escape*.  This function produces output intended to be read by people.

On to print, which behaves just like princ, but posts a newline before the object being printed, and a blank space after it.

Finally, pprint.  This behaves like print, but does not append a trailing blank space.  It also sets the *print-pretty* variable to non-nil, which tries to insert additional whitespace to improve the readability of the output.  Here is an example from the CLHS.  While the output here in SBCL does not match that in the CLHS, you can see that the variable has an effect:
 

CL-USER> (let ((*print-pretty* nil))
           (progn (write '(let ((a 1) (b 2) (c 3)) (+ a b c))) nil))
(LET ((A 1) (B 2) (C 3)) (+ A B C))
NIL
CL-USER> (let ((*print-pretty* t))
           (progn (write '(let ((a 1) (b 2) (c 3)) (+ a b c))) nil))
(LET ((A 1) (B 2) (C 3))
  (+ A B C))
NIL

So, why do you need to know this?  Well, sometimes you’ve seen me writing specialized print-object methods for classes.  These methods should properly inspect the value of *print-readably* and *print-escape*, and take special action in those cases.  Rather than producing an output suitable for a human, it should use something like the make-load-form and make-load-form-saving-slots functions we reviewed earlier so as to produce an output suitable for the desired context.

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.