Category Archives: Uncategorized

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

We continue through our list of more obscure parts of Lisp, and our current focus on the pretty-printer, with pprint-indent.

This function must be called inside a pprint-logical-block block.  It has the effect of changing the indentation that will be used by the pretty-printer after the next newline.  There are two ways it can be used, with :BLOCK or with :CURRENT.

When called with the :BLOCK argument, this sets the indentation relative to the first character of the current block.  That is, if the current block can be thought of as a rectangular column between a left indent and a right margin, pprint-indent :BLOCK moves the left margin by an amount equal to its argument.  The argument can be negative, increasing the size of the printable column, but cannot push the left margin to the left of the end of the per-line prefix string, if any.

When called with the :CURRENT argument, this sets the indentation relative to the current position of the cursor on the line.

Captchas enabled

I’ve enabled captchas on the site, using the WordPress plugin here.  The site was gathering a few hundred spams a day for moderation, which is just too annoying.  I’ve selected numbers only, no words, because Chin Yi’s postings are in traditional Chinese , and that’s the native language of this WordPress installation… it might confuse English visitors to be asked to solve for X in:  X – 六 = 9

I’ll be watching the moderation queue for indications of problems.

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.