Common Lisp the Language, 2nd Edition

next up previous contents index
Next: Common Lisp Object Up: Pretty Printing Previous: Compiling Format Control

27.6. Pretty Printing Dispatch Tables

When *print-pretty* is not nil, the pprint dispatch table in the variable *print-pprint-dispatch* controls how objects are printed. The information in this table takes precedence over all other mechanisms for specifying how to print objects. In particular, it overrides user-defined print-object methods and print functions for structures. However, if there is no specification for how to pretty print a particular kind of object, it is then printed using the standard mechanisms as if *print-pretty* were nil.

A pprint dispatch table is a mapping from keys to pairs of values. The keys are type specifiers. The values are functions and numerical priorities. Basic insertion and retrieval is done based on the keys with the equality of keys being tested by equal. The function to use when pretty printing an object is chosen by finding the highest priority function in *print-pprint-dispatch* that is associated with a type specifier that matches the object.

copy-pprint-dispatch &optional table

A copy is made of table, which defaults to the current pprint dispatch table. If table is nil, a copy is returned of the initial value of *print-pprint-dispatch*.

pprint-dispatch object &optional table

This retrieves the highest priority function from a pprint table that is associated with a type specifier in the table that matches object. The function is chosen by finding all the type specifiers in table that match the object and selecting the highest priority function associated with any of these type specifiers. If there is more than one highest priority function, an arbitrary choice is made. If no type specifiers match the object, a function is returned that prints object with *print-pretty* bound to nil.

As a second return value, pprint-dispatch returns a flag that is t if a matching type specifier was found in table and nil if not.

Table (which defaults to *print-pprint-dispatch*) must be a pprint dispatch table. Table can be nil, in which case retrieval is done in the initial value of *print-pprint-dispatch*.

When *print-pretty* is t, (write object :stream s) is equivalent to (funcall (pprint-dispatch object) s object).

set-pprint-dispatch type function &optional priority table

This puts an entry into a pprint dispatch table and returns nil. The type must be a valid type specifier and is the key of the entry. The first action of set-pprint-dispatch is to remove any pre-existing entry associated with type. This guarantees that there will never be two entries associated with the same type specifier in a given pprint dispatch table. Equality of type specifiers is tested by equal.

Two values are associated with each type specifier in a pprint dispatch table: a function and a priority. The function must accept two arguments: the stream to send output to and the object to be printed. The function should pretty print the object on the stream. The function can assume that object satisfies type. The function should obey *print-readably*. Any values returned by the function are ignored.

The priority (which defaults to 0) must be a non-complex number. This number is used as a priority to resolve conflicts when an object matches more than one entry. An error is signaled if priority fails to be a non-complex number.

The table (which defaults to the value of *print-pprint-dispatch*) must be a pprint dispatch table. The specified entry is placed in this table.

It is permissible for function to be nil. In this situation, there will be no type entry in table after set-pprint-dispatch is evaluated.

To facilitate the use of pprint dispatch tables for controlling the pretty printing of Lisp code, the type-specifier argument of the function set-pprint-dispatch is allowed to contain the form (cons car-type cdr-type). This form indicates that the corresponding object must be a cons whose car satisfies the type specifier car-type and whose cdr satisfies the type specifier cdr-type. The cdr-type can be omitted, in which case it defaults to t.

The initial value of *print-pprint-dispatch* is implementation-dependent. However, the initial entries all use a special class of priorities that are less than every priority that can be specified using set-pprint-dispatch. This guarantees that pretty printing functions specified by users will override everything in the initial value of *print-pprint-dispatch*.

Consider the following examples. The first form restores *print-pprint-dispatch* to its initial value. The next two forms then specify a special way of pretty printing ratios. Note that the more specific type specifier has to be associated with a higher priority.

(setq *print-pprint-dispatch* 
      (copy-pprint-dispatch nil)) 

(defun div-print (s r colon? atsign?) 
  (declare (ignore colon? atsign?)) 
  (format s "(/ ~D ~D)" (numerator (abs r)) (denominator r))) 

(set-pprint-dispatch 'ratio (formatter "#.~/div-print/")) 

(set-pprint-dispatch '(and ratio (satisfies minusp)) 
  (formatter "#.(- ~/div-print/)") 

(pprint '(1/3 -2/3)) prints: (#.(/ 1 3) #.(- (/ 2 3)))

The following two forms illustrate the specification of pretty printing functions for particular types of Lisp code. The first form illustrates how to specify the traditional method for printing quoted objects using ``''' syntax. Note the care taken to ensure that data lists that happen to begin with quote will be printed readably. The second form specifies that lists beginning with the symbol my-let should print the same way that lists beginning with let print when the initial pprint dispatch table is in effect.

(set-pprint-dispatch '(cons (member quote)) 
  #'(lambda (s list) 
      (if (and (consp (cdr list)) (null (cddr list))) 
          (funcall (formatter "'~W") s (cadr list)) 
          (pprint-fill s list))))) 

(set-pprint-dispatch '(cons (member my-let)) 
  (pprint-dispatch '(let) nil)) 

The next example specifies a default method for printing lists that do not correspond to function calls. Note that, as shown in the definition of pprint-tabular above, pprint-linear, pprint-fill, and pprint-tabular are defined with optional colon and atsign arguments so that they can be used as pprint dispatch functions as well as ~/.../ functions.

  '(cons (not (and symbol (satisfies fboundp)))) 

With a line length of 9, (pprint '(0 b c d e f g h i j k)) prints:

(0 b c d 
 e f g h 
 i j k)

This final example shows how to define a pretty printing function for a user defined data structure.

(defstruct family mom kids) 

(set-pprint-dispatch 'family 
  #'(lambda (s f) 
      (format s "~@<#<~;~W and ~2I~_~/pprint-fill/~;>~:>" 
              (family-mom f) (family-kids f))))

The pretty printing function for the structure family specifies how to adjust the layout of the output so that it can fit aesthetically into a variety of line widths. In addition, it obeys the printer control variables *print-level*, *print-length*, *print-lines*, *print-circle*, *print-shared*, and *print-escape*, and can tolerate several different kinds of malformity in the data structure. The output below shows what is printed out with a right margin of 25, *print-pretty* t, *print-escape* nil, and a malformed kids list.

(write (list 'principal-family 
             (make-family :mom "Lucy" 
                          :kids '("Mark" "Bob" . "Dan"))) 
       :right-margin 25 :pretty T :escape nil :miser-width nil) 

 #<Lucy and 
     Mark Bob . Dan>)

Note that a pretty printing function for a structure is different from the structure's print function. While print functions are permanently associated with a structure, pretty printing functions are stored in pprint dispatch tables and can be rapidly changed to reflect different printing needs. If there is no pretty printing function for a structure in the current print dispatch table, the print function (if any) is used instead.


next up previous contents index