Tag Archives: lisp

The less-familiar parts of Lisp for beginners — pprint-fill, pprint-linear, pprint-tabular

We’ve been looking over the pretty-printing functions recently.  These functions, pprint-fill, pprint-linear, and pprint-tabular, are used in the pretty-printing of lists.  Their special behaviour is only manifested when the *print-pretty* variable is non-nil.

The pprint-fill function prints as many list elements on a line as possible before inserting a newline.  Naturally, this raises the question of what the Lisp image thinks is the line length.  The standard defines a variable, *print-right-margin*, which, if set, is taken to be the maximum line length.  If this variable is nil, then the maximum line length is determined in an implementation-defined manner.  If the current cursor position is not at the left margin, the printing is indented appropriately on the left, so that the screen region being considered for printing is narrowed appropriately.

The pprint-linear function will either print all of the list elements on a single line, or if that would exceed the length of the line, prints exactly one element per line.  The behaviour when the cursor is not in the left column is the same as the case above.

The pprint-tabular function prints elements in a tabular fashion, with consecutive elements being printed left-to-right.  The tabsize optional parameter determines the number of spaces from the beginning of one column to the beginning of the next.  The behaviour when the cursor is not in the left column is the same as the case above.

Here are some examples to demonstrate the behaviour of these functions:
*slime-repl sbcl*

CL-USER> (setq *print-pretty* t)
T
CL-USER> (setq *print-right-margin* 50)
50
CL-USER> (let (demolist)
           (dotimes (i 6)
             (push (* i 10000) demolist))
           (princ "Demolist is: ")
           (pprint-linear t demolist))
Demolist is: (50000 40000 30000 20000 10000 0)
NIL
CL-USER> (let (demolist)
           (dotimes (i 7)
             (push (* i 10000) demolist))
           (princ "Demolist is: ")
           (pprint-linear t demolist))
Demolist is: (60000
              50000
              40000
              30000
              20000
              10000
              0)
NIL
CL-USER> (let (demolist)
           (dotimes (i 7)
             (push (* i 10000) demolist))
           (princ "Demolist is: ")
           (pprint-fill t demolist))
Demolist is: (60000 50000 40000 30000 20000 10000
              0)
NIL
CL-USER> (let (demolist)
           (dotimes (i 7)
             (push (* i 10000) demolist))
           (princ "Demolist is: ")
           (pprint-tabular t demolist t nil 10))
Demolist is: (60000     50000     40000     30000
              20000     10000     0)
NIL

The less-familiar parts of Lisp for beginners — pprint-exit-if-list-exhausted

We’re continuing through pretty-printing, and now we’ve come to pprint-exit-if-list-exhausted.  This local macro is only useful in the context of the pprint-logical-block and pprint-pop features, so there will be more discussion of this in upcoming articles.

When, within a pprint-logical-block block, the pprint-exit-if-list-exhausted local macro is invoked, the logical block prints its defined suffix, and then program flow exits the block.  It is forbidden to call pprint-exit-if-list-exhausted when not within a pprint-logical-block block.

The less-familiar parts of Lisp for beginners — pprint-dispatch

As we continue through the list of less familiar features of Common Lisp, we’re going to spend a bit of time in the pretty-printing code.  As you recall from the previous article, pretty-printing is a way to format human-readable output for better legibility, typically by the introduction of additional whitespace.  So, the first function we’ll discuss is pprint-dispatch.  This is not a function that the programmer is likely to invoke directly, but is involved in the handling of the pprint function, or other printing functions with the *print-pretty* variable set to non-nil.  The specific behaviour of pprint-dispatch depends on a standard-defined special variable, *print-pprint-dispatch*.

The table in *print-pprint-dispatch* maps types to functions and corresponding numerical priorities.  There is an implementation-defined startup table that may contain entries, but with priorities guaranteed to be lower than any entry a user might add later with set-pprint-dispatch.

Once more, this is becoming a bit abstract, so I’m now going to describe a typical invocation sequence, as I expect that will make things clearer.

  1. The Lisp instance is asked to print a certain object while *print-pretty* is non-nil.
  2. The Lisp printer calls pprint-dispatch to find the function that it should use for pretty-printing of this type of object.
  3. The type of the object is compared against the types in the table (recall that an object can, and often does, match more than one type).
  4. That entry that has the highest priority is chosen.  This will never be a default function if a user-defined function exists for any type that matches the object.
  5. If there is more than one matching entry with the highest priority (i.e. a tie), one is selected in an implementation-defined manner and returned.
  6. If there is exactly one matching entry with the highest priority, the corresponding function is returned.
  7. If there is no matching entry, the generic non-pretty-printing entry point is returned.  Note, however, that as execution continues in this case, *print-pretty* still non-nil, so user-defined methods like print-object still have an opportunity to act differently based on that variable, if so desired.

A typical invocation construct, then, is (funcall (pprint-dispatch obj) stream obj), but recall that this is likely not to be invoked explicitly by the programmer, as the implementation does this internally.

Points to remember:

  • Entries in the *print-pprint-dispatch* table take priority over print-object methods specialized on the class, even if the former is as a result of matching a less-specific class than the latter.
  • When creating programmer-defined entries, you must avoid assigning the same priority to two different types if there is a way for a single object to match both types, as the behaviour in that case is implementation-defined.
  • There are two ways that *print-pretty* can change the printing behaviour.  The first, when a matching type is found in *print-pprint-dispatch*, and the second when there is no matching type in the table, but a user-defined print-object method inspects that variable and changes its behaviour accordingly.

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.