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.
- The Lisp instance is asked to print a certain object while *print-pretty* is non-nil.
- The Lisp printer calls pprint-dispatch to find the function that it should use for pretty-printing of this type of object.
- 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).
- 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.
- 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.
- If there is exactly one matching entry with the highest priority, the corresponding function is returned.
- 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.