CLHS: Issue PRETTY-PRINT-INTERFACE Writeup

#

## Issue PRETTY-PRINT-INTERFACE Writeup

    Version 3 (by Guy Steele Jr) supersedes version 2 and is changed from
    version 1 as follows: adds a functional interface to supplement the
    interface through FORMAT, and reflects comments by Barrett and
    Pierson.

    Version 4 (by Dick Waters) is changed from version 3 as follows: The
    short summary is updated to reflect the functional interface.  The
    functional interface is changed following suggestions made by Dave Moon.
    The proposal is amended in a few minor ways to increase the
    compatibility with variable width fonts.  Additional discussion has been
    added with regard to the advantages of XP with respect to handling
     detection and abbreviation, the interaction with CLOS, and
    the extended type specifier CONS used by XP.

    Version 5 (by Waters, Haflich, Laubsch, Loosemore, Pierson, and van Roggen).
      On June 27 it was voted 12 to 4 in a plenary session of X3J13 that an add
    hoc committee on pretty printing be created containing the above members
    with the charter of ironing out the final details of the pretty print
    interface and that if this committee could come to a unanimous decision on
    the interface, the interface would become part of the draft Common Lisp
    standard without the need of a further vote by the full committee.  The
    unanimity of the add hoc committee has been signaled by having each member
    of the committee send a message to X3J13 to this effect.
      In addition to the main vote above, five straw polls were taken.  Three
    of these votes were overwhelming; however, two appeared too close to be
    definitive.  In order of the clarity of the outcome the straw votes were:
     16-0 in favor of the basic functional interface,
     10-4 against the inclusion of #"...",
     12-7 in favor of having some way to convert a FORMAT string into a function,
     11-8 in favor of allowing FORMAT to take a function as well as a string,
     10-8 against the pretty printer extensions of FORMAT.
    The subcommittee followed these recommendations except for the last.
    (See the end of the discussion section.)
      Version 5 is changed from version 4 as follows:  The names of the
    functions and variables have been changed to better reflect that (for the
    most part) they are a group that applies only to pretty printing.  The
    variables *DEFAULT-RIGHT-MARGIN* and *LAST-ABBREVIATED-PRINTING* have been
    eliminated.  The readmacro construct #"..." has been eliminated and a new
    macro FORMATTER introduced in its place.  The way *PRINT-LINES*
    abbreviation is indicated has been improved to ensure that the delimiters
    will be balanced in the output and to ensure that the reader will complain
    if you later try to read the output.  The macros LOGICAL-BLOCK-POP and
    LOGICAL-BLOCK-COUNT have been eliminated and the functionality they
    supported provided in a slightly different form by two new macros
    PPRINT-POP and PPRINT-EXIT-IF-LIST-EXHAUSTED.  The macro
    DEFINE-PRINT-DISPATCH has been replaced by a function SET-PPRINT-DISPATCH.
    The proposal has been reorganized to show that the functional interface is
    more fundamental than the FORMAT interface, to clarify exactly what happens
    in error situations, and to indicate that the examples are merely examples
    and not part of the proposal.

    ================================================================================

    Issue:		PRETTY-PRINT-INTERFACE

    References:	Description of XP by Dick Waters (attached)
    		*PRINT-PRETTY* (CLtL p. 371)
    		WRITE (CLtL p. 382)
    		PPRINT (CLtL p. 383)
    		FORMAT (CLtL pp. 385-407)
    		FORMAT ~T directive (CLtL pp. 398-399)
    		FORMAT ~< directive (CLtL pp. 404-406)

    Related issues:

    Category:	CLARIFICATION CHANGE ADDITION

    Edit history:	Version 1, 24-Feb-89 by Steele
    		Version 2, 15-Mar-89 by Steele and Waters
    		Version 3, 15-Mar-89 by Steele
    		Version 4, 22-Mar-89 by Waters
    		Version 5, 20-Jul-89 by Waters et al

    Problem description:
     At present, Common Lisp provides no specification whatsoever of how
    pretty-printing is to be accomplished, and no way for the user to control
    it.  In particular, there is no protocol by which a user can write a
    print-function for a structure, or a method for PRINT-OBJECT, that will
    interact smoothly with the built-in pretty-printer in a portable manner.

    Proposal (PRETTY-PRINT-INTERFACE:XP):

    Adopt the interfaces and protocols of the XP pretty-printer by Dick Waters,
    described in full in the attached document.  Here is a very brief summary
    of the proposal.

    New variables:	*PRINT-PPRINT-DISPATCH*
    		*PRINT-MISER-WIDTH*
    		*PRINT-RIGHT-MARGIN*
    		*PRINT-LINES*

    New functions:	COPY-PPRINT-DISPATCH
    		SET-PPRINT-DISPATCH
    		PPRINT-DISPATCH
    		PPRINT-NEWLINE
    		PPRINT-TAB
    		PPRINT-INDENT
    		PPRINT-FILL
    		PPRINT-LINEAR
    		PPRINT-TABULAR

    New macros:	PPRINT-LOGICAL-BLOCK
    		PPRINT-POP
    		FORMATTER

    New FORMAT directives:	~W  ~_  ~I  ~:T  ~/name/  ~<...~:>

    The function WRITE is extended to accept four additional keyword arguments
    :PPRINT-DISPATCH, :RIGHT-MARGIN, :LINES, and :MISER-WIDTH corresponding to
    the four new variables.

    The function FORMAT is extended so that it can accept a function instead of
    a FORMAT string.  (This change is also made in the other functions that
    accept FORMAT strings such as ERROR and WARN.)

    Examples:	See attached document.

    Rationale:
     There ought to be a good user interface to the pretty printer.
    This is the only proposal for which there is a portable implementation
    that has seen extensive use and is being made freely available.

    Current practice:
     XP son of PP son of GPRINT son of #PRINT is the latest in a line of pretty
    printers that goes back 13 years.  All of these printers use essentially
    the same basic algorithm and conceptual interface.  Further, except for
    #PRINT, which was implemented solely to satisfy the author's personal
    needs, each of these printers has had extensive use.  XP has been in
    experimental use as the pretty printer in CMU Common Lisp for 6 months.  PP
    has been the pretty printer in DEC Common Lisp for the past 3 years.  Prior
    to three years ago, GPRINT was used for 2 years as the pretty printer in
    DEC Common Lisp.  In addition, GPRINT has been the pretty printer in
    various generations of Symbolics Lisp for upwards of 5 years.
    (See Waters R.C., "User Format Control in a Lisp Prettyprinter", ACM TOPLAS,
    5(4):513--531, October 1983.)

    Cost to Implementors:  A fair amount of effort (several man-weeks at most).
     Source code for XP is available to all comers from Dick Waters.  It has
    been arranged with MIT for anyone who wants to to get a non-exclusive
    royalty-free license for XP from MIT.  The system is documented in great
    detail in:
      Waters, Richard C., "XP: A Common Lisp Pretty Printing System",
      Artificial Intelligence Laboratory Technical Memo 1102a,
      Massachusetts Institute of Technology, Cambridge MA, July 1989.

    Cost to Users:  None (I think).  This is an upward-compatible extension.
     Cost of non-adoption:  Continued inability for user print-functions
    to interact with the pretty-printer in a useful and portable manner.

    Performance impact:  XP is claimed to be quite fast.

    Benefits:  User control of pretty-printing in a portable manner.

    Aesthetics:
     Using ~<...~:> may strike some as uncomfortably close in the syntactic
    space of FORMAT directives to the existing ~<...~>.  However, it is very
    unlikely that both of these directives (pretty-print logical block and
    columnar justification, respectively) will be used in the same call to
    FORMAT.  Previous versions of XP used ~!...~. instead of ~<...~:> but this
    made FORMAT strings very difficult to read; it is preferable to have a
    directive that looks like matching brackets of some sort.

    Dan Pierson comments:  You might mention that some people will undoubtedly
    find piling more hair on FORMAT ugly (of course these same people may
    well find FORMAT in general ugly :-)).

    Discussion:

    Zetalisp used ~:T to mean pixelwise tabulation, so the use of ~:T
    suggested here may be a problem.  If so, another suggestion for naming
    this directive would be appreciated.

    The ~/.../ directive is already in Zetalisp, and is not an idea new
    to this proposal.  However, it should be noted that the proposal for
    ~/.../ here is simpler than, and incompatible with, the current Zatalisp
    practice.

    Guy Steele and Dick Waters strongly support this proposal.  (As an example,
    Guy Steele has a portable simulator for Connection Machine Lisp, and would
    like very much to have xappings and xectors pretty-print properly.)

    ---

    Dan Pierson comments: You can add me to the list of strong supporters of
    this proposal.  While the proposal is long and complex, it is supported by
    a long history of usage in several different Lisp environments.

    The utility of *PRINT-LINES* becomes more obvious if it is pointed out
    that Dick's pretty printers are implemented to print each line as it
    is computed.  This means that a small value for *PRINT-LINES* saves
    significant time as well as output medium space.

    The advantages of compiled format strings (format functions) should be
    brought out as benefits in their own right.  The current proposal just
    mentions them as a minor feature of XP.

    At first this struck me a very cute end run around the failure of
    STREAM-INFO, then I realized that one of the problems with STREAM-INFO
    may have been that it was a standard at the wrong level.  STREAM-INFO
    permitted people to use XP, but not to count on it.  This proposal
    makes it possible to write portable code whose new data structures and
    language elements print correctly in whatever Common Lisp environment
    they're run in.  [End of comments by Pierson]

    ---

    It has been noted by Guy Steele that some places in the initial document
    where it says that circularity detection is handled correctly, this is
    true a fortiori following the decision on PRINT-CIRCLE-STRUCTURE.
    However, Waters notes that the vote on PRINT-CIRCLE-STRUCTURE said
    nothing about the handling of *PRINT-LEVEL*.  Therefore, the fact that
    XP handles *PRINT-LEVEL* correctly is an improvement.

    In addition, PRINT-CIRCLE-STRUCTURE is also silent on what is supposed
    to happen if a user program decomposes a list itself (e.g., with DOLIST
    or ~{~}) rather than calling a print function.  Assumedly *PRINT-CIRCLE*
    etc. is not handled in this case.  In contrast, if one uses
    PPRINT-LOGICAL-BLOCK or ~<~:>, then *PRINT-CIRCLE*, *PRINT-LEVEL*, and
    *PRINT-LENGTH* are all automatically handled correctly.

    For example, (format nil "-~1{~A ~A ~A ~A ~A ~}-" '#1=(1 #1# 2 . #1#))
    produces "-1 #1=(1 #1# 2 . #1#) 2 1 #1=(1 #1# 2 . #1#) -"
    even under PRINT-CIRCLE-STRUCTURE and
    (format nil "-~1{~A ~}-" '#1=(1 #1# 2 . #1#))
    causes infinite looping.  However, in XP,
    (format nil "-~:<~W ~W ~W ~W ~W~:>-" '#1=(1 #1# 2 . #1#))
    produces "-#1=(1 #1# 2 . #1#)-".
    This proves to be very useful when writing pretty printing functions for
    things.  Note also that ~<~:> supports *PRINT-LEVEL* and *PRINT-LENGTH*
    correctly.  All the same things can be said about the functional interface
    and using PPRINT-LOGICAL-BLOCK rather than traversing a list yourself in
    some fashion.

    All in all, Waters claims that PRINT-CIRCLE-STRUCTURE covers at most 1/4
    of what XP does in support of *PRINT-CIRCLE* and does not cover anything
    of what XP does to support *PRINT-LEVEL*, *PRINT-LENGTH*, and
    robustness in the face of malformed arguments.  These are vital
    features for writing printing functions that really work right all the time.

    ---

    It has been suggested that objects should be looked up in pprint dispatch
    tables by looking for the most specific type specifier that applies, rather
    than looking for the highest priority type specifier that applies.  There
    are two problems with this.  First, it is possible for two type specifiers
    to apply without one being more specific than the other.  For example,
    consider the type specifiers (OR INTEGER FLOAT) and (OR INTEGER RATIONAL)
    or the type specifiers (NOT COMPLEX) and (NOT INTEGER).

    A much more serious problem is that the only way to tell whether one type
    specifier is more specific than another is to use SUBTYPEP.  Unfortunately,
    even with the clarifications and extensions introduced by X3J13 with regard
    to SUBTYPEP in particular and the type system in general, SUBTYPEP is not
    very dependable.  There are critical situations where it cannot reasonably
    work in any implementation (e.g., when a type specifier contains SATISFIES)
    and many other situations where it can vary from one implementation to
    another.  Unfortunately, pprint dispatch tables are expected to contain
    type specifiers containing SATISFIES on a regular basis.  In addition, the
    variability in SUBTYPEP would reduce the portability of user-defined pprint
    dispatch tables if dispatching through them depended on SUBTYPEP.  All in
    all, it seems much better for the pretty printing to rely on priorities
    rather than on SUBTYPEP

    ---

    It has been noted by Dave Moon that things would be much more elegant if
    SET-PPRINT-DISPATCH could be expressed directly as a CLOS DEFMETHOD for
    an appropriate generic function.  Dick Waters agrees with this.  However,
    SET-PPRINT-DISPATCH depends on type specifiers that are more complex than
    the ones CLOS deals with and ones that do not have clear subtype/supertype
    relationships, compensating for the latter problem by supporting numerical
    priorities to disambiguate things.  (The defaulting behavior is a key
    feature of the pretty printer.)  At the very least, this means that
    SET-PPRINT-DISPATCH will not fit into CLOS in a simple way.

    Given the problems, Moon suggests that "it does seem that right now it
    might be best to keep a separate SET-PPRINT-DISPATCH macro, with the
    idea that the expansion is implementation-dependent at the moment, but
    might some day be changed to be defined to expand into DEFMETHOD.  I
    haven't looked to see whether any syntactic changes would be appropriate
    to make that transition smoother."

    (Waters also worries that the overhead needed to locate the right CLOS
    method would seriously degrade the pretty printer, because the printer
    has to do this for every part of every object printed.  This dispatching is
    currently done by very fast code that is tuned to take advantage of the
    observed distribution of kinds of objects that have special pretty
    printers attached to them.  Even with this special purpose code,
    dispatching takes a significant part of the pretty printer's time.)

    ---

    Dave Moon also comments that it is not good to have something that looks
    like a type specifier (i.e., the extended form of the CONS type specifier
    used by SET-PPRINT-DISPATCH) and yet is not a real type specifier.  He
    suggests that we should either amend Common Lisp to accept the extended
    form of the CONS type specifier, or stop having SET-PPRINT-DISPATCH use it.

    Many of the members of the ad hoc pretty printing subcommittee agree that
    (CONS car-type cdr-type) should be made a full fledged type specifier.  In
    fact it has been suggested that there should be a much wider variety of
    ways to talk about lists and their contents than currently exists.
    However, the subcommittee feels that it would be going significantly beyond
    our charter to change anything about the type system.

    Therefore, we opted for the opposite tack of making it very clear in this
    proposal that while the function SET-PPRINT-DISPATCH accepts the form
    (CONS car-type cdr-type), this form is not a type specifier.

    ---

    To a considerable extent, the design of the XP interface is completely
    neutral about the issue of variable- versus fixed- width fonts.  In
    particular, most of the discussion of how the formating proceeds either
    talks about absolute positions of zero or talks about something being
    in the same horizontal position as something else.  These statements are
    all font-independent.  (Further, although Waters' current implementation
    does not support variable-width fonts, the algorithms used could be
    extended to support them without radical changes.)

    Nevertheless, there are 8 places where users specify explicit non-zero
    lengths: the variables *PRINT-RIGHT-MARGIN* and *PRINT-MISER-WIDTH*,
    the numeric arguments to ~T, ~I, and ~/pprint-tabular/ and their associated
    functions PPRINT-TAB, PPRINT-INDENT, and PPRINT-TABULAR.

    It is proposed that all of these lengths be in the same units, and that
    this unit be ems (the length of an "m" in the font currently being used
    to output characters to the relevant output stream at the moment that
    the command is encountered or a variable is consulted).

    It is further proposed that users and implementors be advised to set things
    up so that explicit lengths do not have to be specified.  For implementors,
    this means making streams smart enough that they know how wide they are.
    (This avoids the use of *PRINT-RIGHT-MARGIN*.)  For users, this means
    relying on streams knowing their own widths (which is a good idea for
    adaptability in any case) and using ~:I (instead of just ~I) to specify
    indentations wherever possible.  Further, it should be noted that since
    *PRINT-MISER-WIDTH* is essentially heuristic in nature, it does not
    matter if its value is only an approximate length and users will only need
    to change the value of *PRINT-MISER-WIDTH* in unusual situations.
    This leaves only tabbing as an area where explicit lengths have to be
    specified on a regular basis.  Fortunately, approximate lengths are often
    acceptable in this situation as well.

    ---

    The currently proposed syntax for PPRINT-LOGICAL-BLOCK was suggested by
    Dave Moon based on his experience with similar constructs at Symbolics.
    Waters had originally suggested using a standard binding pair to specify
    the underlying stream separately from the variable used to hold the special
    stream used within the logical block.  However, this invites user mistakes.
    The problem is that peculiar results will be obtained if any I/O is sent
    directly to the underlying stream from within the logical block.  By
    arranging the syntax as proposed, the same variable is always used for the
    special stream within the logical block as is used for the underlying
    stream outside of the block.  As a result, it is syntactically impossible
    to directly access the underlying stream (unless it is stored in two
    different variables) and the user is painlessly saved from making mistakes.
    Also, the PPRINT-LOGICAL-BLOCK macro is rendered more compact.

    ---

    Another interesting issue is the interaction between pretty-printing and
    *PRINT-READABLY*.  Note first, that WITH-STANDARD-IO-SYNTAX binds
    *PRINT-PRETTY* to NIL.  In general one should expect that maximum machine
    readability will be achieved when *PRINT-PRETTY* is nil.  However, in
    analogy with issue DATA-IO, it is reasonable to require that
    PPRINT-DISPATCH functions must obey *PRINT-READABLY*.  Note that XP
    supports a number of features (e.g., automatic handling of malformed lists)
    that are explicitly designed to make it easier to write pprint functions
    that reliably produce machine readable output.

    ---

    Steve Haflich comments that the macro FORMATTER provides an opportunity at
    some later time to do something about making FORMAT control strings more
    readable.  Specifically, when using FORMATTER, one should not hesitate to
    insert lots of newlines, because they will not waste any space since the
    FORMAT string will be converted into a function in any case.  Second, it
    would be nice to add some feature to format strings that would allow
    comments to appear in a FORMAT string.  (This is what ~; probably should
    have been reserved to mean.)

    ---

    Walter van Roggen notes that the automatic handling of *PRINT-LEVEL* by XP
    obsoletes the need for the third argument to print functions for
    structures.  (This argument was very seldom used in any case.)  It might be
    appropriate at some future time to get rid of the third argument to print
    functions for structures.  However, this would not be an upward compatible
    change.

    ---

    The pretty printer control interface is included in this proposal for two
    key reasons:

    (A) Something like the following is probably a key part of the argument
    against the pretty-printer-control FORMAT interface in the minds of those
    that oppose it.  "Including the pretty-printer-control FORMAT interface
    might be a gain or a loss for Common Lisp, but nothing could possibly be
    lost by not including it.  You can always just use FORMAT as it is now, and
    you can get pretty printing control with the new functions."  However, this
    argument is not quite true.

    As can be seen in the example below, pretty printer control information has
    to be highly intertwined with the rest of the output functions.  As a
    practical matter, this means that if there is no pretty-printer-control
    FORMAT interface you cannot make much if any use of FORMAT when specifying
    output that contains pretty-printer-control information.  (For instance in
    the 20 line example below, there is not even one place where there are two
    printing operations in a row that could be combined into a call on FORMAT
    if there were no pretty-printer-control FORMAT interface.  This means that
    there would be no benefit to using FORMAT anywhere in this example.)

    The consequence of the above is that, even though pretty printer control
    and FORMAT inherently have nothing to do with each other, adding pretty
    printer control facilities without adding a pretty-printer-control FORMAT
    interface, effectively requires people to choose between FORMAT and pretty
    printer control.  In order to allow people to separately decide whether
    they do or do not want to user FORMAT and do or do not want to get involved
    with controlling the pretty printer, you have to provide a
    pretty-printer-control FORMAT interface.

    This issue is intensified given a pretty printer such as XP that is
    within epsilon of just as fast as the ordinary non-pretty printer,
    because many people choose to set *PRINT-PRETTY* to T all of the time.
    They are therefore naturally led to wanting to specify at least some
    pretty printing control information whenever defining a print function
    for a structure, or an error message, or in fact essentially any kind
    of output.  If there is no pretty-printer-control FORMAT interface,
    then this would effectively bar the use of FORMAT for these users.

    In summary, rather than pushing FORMAT into new territory, the
    pretty-printer-control FORMAT interface is required to prevent FORMAT from
    sliding into obsolescence.

    (B) Although it probably is not convincing to those that do not like
    FORMAT, a additional reason for having a pretty-printer-control FORMAT
    interface is exactly the same as the reason for having FORMAT at all:
    compactness.  For example, as shown in the main proposal, the format string:

      "~:<~W~^ ~:<~@{~:<~@{~W~^ ~_~}~:>~^ ~:_~}~:>~1I~@{~^ ~_~W~}~:>"

    is the same as the expression:

      (pprint-logical-block (nil list :prefix "(" :suffix ")")
        (write (pprint-pop))
        (pprint-exit-if-list-exhausted)
        (write-char #\space)
        (pprint-logical-block (nil (pprint-pop) :prefix "(" :suffix ")")
          (pprint-exit-if-list-exhausted)
          (loop (pprint-logical-block (nil (pprint-pop) :prefix "(" :suffix ")")
    	      (pprint-exit-if-list-exhausted)
    	      (loop (write (pprint-pop))
    		    (pprint-exit-if-list-exhausted)
    		    (write-char #\space)
    		    (pprint-newline :linear)))
    	    (pprint-exit-if-list-exhausted)
    	    (write-char #\space)
    	    (pprint-newline :fill)))
        (pprint-indent :block 1)
        (loop (pprint-exit-if-list-exhausted)
    	  (write-char #\space)
    	  (pprint-newline :linear)
    	  (write (pprint-pop)))))

    It is of course also true that a line of FORMAT control string is a
    lot harder to read than a line of Lisp code.  However, FORMAT has
    become a permanent fixture of Common Lisp, because a line of FORMAT
    control string is a lot easier to read and work with than 20 lines of
    Lisp code.

    (C) It should also be noted that the way the XP proposal was first written
    up made the FORMAT interface look a lot more complex than it is.  Only six
    things are being added.  Two of these just fix problems with FORMAT and do
    not have anything to do with pretty printing.  Of the remaining four, three
    are very simple.  Only ~<...~:> could be called complex.

    ~A and ~S both force the value of *PRINT-ESCAPE*.  ~W is needed in
    order to write FORMAT strings that obey *PRINT-ESCAPE*.

    ~/.../ restores (in a simplified form) a feature of FORMAT that was
    lost in the first version of Common Lisp.  A number of people have
    commented that this is something that they like, having nothing to do
    with pretty printing.  (Note that ~/PPRINT-LINEAR/, ~/PPRINT-FILL/,
    and ~/PPRINT-TABULAR/ are not new directives, they are just a
    consequence of the fact that ~/.../ can call the functions
    PPRINT-LINEAR, PPRINT-FILL, and PPRINT-TABULAR.)

    ========================= ``Attached Document'' =========================

    Proposal (PRETTY-PRINT-INTERFACE:XP):

    Full description of XP accompanying version 5 of the proposal

    --------------------

    			   Pretty Printing

    			  Richard C. Waters

    Pretty printing has traditionally been a black box process, displaying
    program code using a set of fixed layout rules.  Its utility can be greatly
    enhanced by opening it up to user control.

    By providing direct access to the mechanisms within the pretty printer that
    make dynamic decisions about layout, the macros and functions
    PPRINT-LOGICAL-BLOCK, PPRINT-NEWLINE, and PPRINT-INDENT make it possible to
    specify pretty printing layout rules as a part of any function that
    produces output.  They also make it very easy for the detection of
    circularity and sharing, and abbreviation based on length and nesting depth
    to be supported by the function.  The function SET-PPRINT-DISPATCH makes it
    possible to associate a user-defined pretty printing function with any type
    of object.  Together, these facilities enable users to redefine the way
    code is displayed and allow the full power of pretty printing to be applied
    to complex combinations of data structures.

    		  Pretty Printing Control Variables

    *PRINT-RIGHT-MARGIN*                                             [variable]

    A primary goal of pretty printing is to keep the output between a pair of
    margins.  The left margin is set at the column where the output begins.  If
    this cannot be determined, the left margin is set to zero.

    When *PRINT-RIGHT-MARGIN* is not NIL, it specifies the right margin to use
    when making layout decisions.  When *PRINT-RIGHT-MARGIN* is NIL (the
    initial value), the right margin is set at the maximum line length that can
    be displayed by the output stream without wraparound or truncation.  If
    this cannot be determined, the right margin is set to an implementation
    dependent value.

    To allow for the possibility of variable-width fonts, *PRINT-RIGHT-MARGIN*
    is interpreted in terms of ems---the length of an "m" in the font being
    used to display characters on the relevant output stream at the moment when
    the variables are consulted.

    *PRINT-MISER-WIDTH*                                              [variable]

    If *PRINT-MISER-WIDTH* is not NIL, the pretty printer switches to a compact
    style of output (called miser style) whenever the width available for
    printing a substructure is less than or equal to *PRINT-MISER-WIDTH* ems.
    The initial value of *PRINT-MISER-WIDTH* is implementation-dependent.

    *PRINT-LINES*                                                    [variable]

    When given a value other than its initial value of NIL, *PRINT-LINES*
    limits the number of output lines produced when something is pretty
    printed.  If an attempt is made to go beyond *PRINT-LINES* lines, "
    .." is printed at the end of the last line followed by all of the
    suffixes (closing delimiters) that are pending to be printed.

    (let ((*print-right-margin* 25) (*print-lines* 3))
      (pprint '(progn (setq a 1 b 2 c 3 d 4))))

    (PROGN (SETQ A 1
                 B 2
                 C 3 ..))

    (The symbol ".." is printed out to ensure that a reader error will occur
    if the output is later read.  A symbol different than "..." is used to
    indicate that a different kind of abbreviation has occurred.)

    *PRINT-PPRINT-DISPATCH*                                          [variable]

    When *PRINT-PRETTY* is not NIL, printing is controlled by the `pprint
    dispatch table' stored in the variable *PRINT-PPRINT-DISPATCH*.  The
    initial value of *PRINT-PPRINT-DISPATCH* is implementation dependent and
    causes traditional pretty printing of Lisp code.  The last section of this
    proposal explains how the contents of this table can be changed.

    The function WRITE accepts keyword arguments :PPRINT-DISPATCH,
    :RIGHT-MARGIN, :LINES, and :MISER-WIDTH corresponding to
    *PRINT-PPRINT-DISPATCH*, *PRINT-RIGHT-MARGIN*, *PRINT-LINES*, and
    *PRINT-MISER-WIDTH*.

    	     Dynamic Control of the Arrangement of Output

    The following functions and macros support precise control of what should
    be done when a piece of output is too large to fit in the space available.
    Three concepts underlie the way these operations work---`logical blocks',
    `conditional newlines', and `sections'.  Before proceeding further, it is
    important to define these terms.

    The first line of Figure 1 shows a schematic piece of output.  The
    characters in the output are represented by "-"s.  The positions of
    conditional newlines are indicated by digits.  The beginnings and ends of
    logical blocks are indicated by "<" and ">" respectively.

    The output as a whole is a logical block and the outermost section.  This
    section is indicated by the 0's on the second line of Figure 1.  Logical
    blocks nested within the output are specified by the macro
    PPRINT-LOGICAL-BLOCK.  Conditional newline positions are specified by calls
    on PPRINT-NEWLINE.  Each conditional newline defines two sections (one
    before it and one after it) and is associated with a third (the section
    immediately containing it).

    The section after a conditional newline consists of: all the output up to,
    but not including, (a) the next conditional newline immediately contained
    in the same logical block; or if (a) is not applicable, (b) the next
    newline that is at a lesser level of nesting in logical blocks; or if (b)
    is not applicable, (c) the end of the output.

    The section before a conditional newline consists of: all the output back
    to, but not including, (a) the previous conditional newline that is
    immediately contained in the same logical block; or if (a) is not
    applicable, (b) the beginning of the immediately containing logical block.
    The last four lines in Figure 1 indicate the sections before and after the
    four conditional newlines.

    The section immediately containing a conditional newline is the shortest
    section that contains the conditional newline in question.  In Figure 1,
    the first conditional newline is immediately contained in the section
    marked with 0's, the second and third conditional newlines are immediately
    contained in the section before the fourth conditional newline, and the
    fourth conditional newline is immediately contained in the section after
    the first conditional newline.

                     <-1---<--<--2---3->--4-->->
                     000000000000000000000000000
                     11 111111111111111111111111
                               22 222
                                  333 3333
                            44444444444444 44444

    Figure 1: Example of logical blocks, conditional newlines, and sections.

    Whenever possible, the pretty printer displays the entire contents of a
    section on a single line.  However, if the section is too long to fit in
    the space available, line breaks are inserted at conditional newline
    positions within the section.

    PPRINT-NEWLINE kind &OPTIONAL (stream *STANDARD-OUTPUT*)    [Function]

    STREAM (which defaults to *STANDARD-OUTPUT*) follows the standard
    conventions for stream arguments to printing functions (i.e., NIL stands
    for *STANDARD-OUTPUT* and T stands for *TERMINAL-IO*).  The KIND argument
    specifies the style of conditional newline.  It must be one of :LINEAR,
    :FILL, :MISER, or :MANDATORY.  An error is signalled if any other value is
    supplied.  If STREAM is a pretty printing stream created by
    PPRINT-LOGICAL-BLOCK, a line break is inserted in the output when the
    appropriate condition below is satisfied.  Otherwise, PPRINT-NEWLINE has no
    effect.  The value NIL is always returned.

    If KIND is :LINEAR, it specifies a `linear-style' conditional newline.  A
    line break is inserted if and only if the immediately containing section
    cannot be printed on one line.  The effect of this is that line breaks are
    either inserted at every linear-style conditional newline in a logical
    block or at none of them.

    If KIND is :MISER, it specifies a `miser-style' conditional newline.  A
    line break is inserted if and only if the immediately containing section
    cannot be printed on one line and miser style is in effect in the
    immediately containing logical block.  The effect of this is that
    miser-style conditional newlines act like linear-style conditional
    newlines, but only when miser style is in effect.  Miser style is in effect
    for a logical block if and only if the starting position of the logical
    block is less than or equal to *PRINT-MISER-WIDTH* from the right margin.

    If KIND is :FILL, it specifies a `fill-style' conditional newline.  A line
    break is inserted if and only if either (a) the following section cannot be
    printed on the end of the current line, (b) the preceding section was not
    printed on a single line, or (c) the immediately containing section cannot
    be printed on one line and miser style is in effect in the immediately
    containing logical block.  If a logical block is broken up into a number of
    subsections by fill-style conditional newlines, the basic effect is that
    the logical block is printed with as many subsections as possible on each
    line.  However, if miser style is in effect, fill-style conditional
    newlines act like linear-style conditional newlines.

    If KIND is :MANDATORY, it specifies a `mandatory-style' conditional
    newline.  A line break is always inserted.  This implies that none of the
    containing sections can be printed on a single line and will therefore
    trigger the insertion of line breaks at linear-style conditional newlines
    in these sections.

    When a line break is inserted by any type of conditional newline, any
    blanks that immediately precede the conditional newline are omitted from
    the output and indentation is introduced at the beginning of the next line.
    By default, the indentation causes the following line to begin in the same
    horizontal position as the first character in the immediately containing
    logical block.  (The indentation can be changed via PPRINT-INDENT.)

    There are a variety of ways unconditional newlines can be introduced into
    the output (e.g., via TERPRI or by printing a string containing a newline
    character).  As with mandatory conditional newlines, this prevents any of
    the containing sections from being printed on one line.  In general, when
    an unconditional newline is encountered, it is printed out without
    suppression of the preceding blanks and without any indentation following
    it.  However, if a per-line prefix has been specified (see
    PPRINT-LOGICAL-BLOCK), this prefix will always be printed no matter how a
    newline originates.

    PPRINT-LOGICAL-BLOCK (stream-symbol list                     [Macro]
                          &KEY :prefix :per-line-prefix :suffix)
                         &BODY body

    This macro causes printing to be grouped into a logical block.  The value
    NIL is always returned.

    STREAM-SYMBOL must be a symbol.  If it is NIL, it is treated the same as
    if it were *STANDARD-OUTPUT*.  If it is T, it is treated the same as if
    it were *TERMINAL-IO*.  The run-time value of STREAM-SYMBOL must be a
    stream (or NIL standing for *STANDARD-OUTPUT* or T standing for *TERMINAL-IO*).
    The logical block is printed into this destination stream.

    The BODY can contain any arbitrary Lisp forms.  Within the BODY,
    STREAM-SYMBOL (or *STANDARD-OUTPUT* if STREAM-SYMBOL is NIL, or
    *TERMINAL-IO* if STREAM-SYMBOL is T) is bound to a `pretty printing' stream
    that supports decisions about the arrangement of output and then forwards
    the output to the destination stream.  All the standard printing functions
    (e.g., WRITE, PRINC, TERPRI) can be used to print output the pretty
    printing stream created by PPRINT-LOGICAL-BLOCK.  All and only the output
    sent to this pretty printing stream is treated as being in the logical
    block.

    PPRINT-LOGICAL-BLOCK and the pretty printing stream it creates have dynamic
    extent.  It is undefined what happens if output is attempted outside of
    this extent to the pretty printing stream created.  It is unspecified what
    happens if, within this extent, any output is sent directly to the
    underlying destination stream.

    The :SUFFIX, :PREFIX, and :PER-LINE-PREFIX must all be expressions that (at
    run time) evaluate to strings.  :SUFFIX (which defaults to the null string)
    specifies a suffix that is printed just after the logical block.  The
    :PREFIX and :PRE-LINE-PREFIX arguments are mutually exclusive.  If neither
    :PREFIX or :PER-LINE-PREFIX is specified, a :PREFIX of the null string is
    assumed.  :PREFIX specifies a prefix to be printed before the beginning of
    the logical block.  :PER-LINE-PREFIX specifies a prefix that is printed
    before the block and at the beginning of each new line in the block.  An
    error is signalled if :PREFIX and :PRE-LINE-PREFIX are both used or if the
    :SUFFIX, :PREFIX, or :PER-LINE-PREFIX do not evaluate to strings.

    LIST is interpreted as being a list that BODY is responsible for
    printing.  (See PPRINT-EXIT-IF-LIST-EXHAUSTED and PPRINT-POP.)  If
    LIST does not (at run time) evaluate to a list, it is printed using
    WRITE.  (This makes it easier to write printing functions that are
    robust in the face of malformed arguments.)  If *PRINT-CIRCLE* (and
    possibly *PRINT-SHARED*) is not NIL and LIST is a circular (or shared)
    reference to a cons, then an appropriate #n# marker is printed.  (This
    makes it easy to write printing functions that provide full support
    for circularity and sharing abbreviation.)  If *PRINT-LEVEL* is not
    NIL and the logical block is at a dynamic nesting depth of greater
    than *PRINT-LEVEL* in logical blocks, # is printed.  (This makes easy
    to write printing functions that provide full support for depth
    abbreviation.)

    If either of the three conditions above occurs, the indicated output is
    printed on STREAM-SYMBOL and the BODY is skipped along with the printing of
    the :PREFIX and :SUFFIX.  (If the BODY is not responsible for printing a
    list, then the first two tests above can be turned off by supplying NIL for
    the LIST argument.)

    In addition to the LIST argument of PPRINT-LOGICAL-BLOCK, the arguments of
    the standard printing functions such as WRITE, PRINT, PPRINT, PRINT1, and
    PPRINT, as well as the arguments of the standard FORMAT directives such as
    ~A, ~S, (and ~W) are all checked (when necessary) for circularity and
    sharing.  However, such checking is not applied to the arguments of the
    functions WRITE-LINE, WRITE-STRING, and WRITE-CHAR or to the literal text
    output by FORMAT.  A consequence of this is that you must use one of the
    latter functions if you want to print some literal text in the output that
    is not supposed to be checked for circularity or sharing.  (See the
    examples below.)

    ----------------------------------------

    Implementation note:  detection of circularity and sharing is supported by
    the pretty printer by in essence performing requested output twice.
    On the first pass, circularities and sharing are detected and the
    actual outputting of characters is suppressed.  On the second pass, the
    appropriate #n= and #n# markers are inserted and characters are output.

    A consequence of this two-pass approach to the detection of circularity and
    sharing is that the BODY of a PPRINT-LOGICAL-BLOCK must not perform any
    side-effects on the surrounding environment.  This includes not modifying
    any variables that are bound outside of its scope.  Obeying this
    restriction is facilitated by using PPRINT-POP, instead of an ordinary POP
    when traversing a list being printed by the BODY of a
    PPRINT-LOGICAL-BLOCK.)

    ----------------------------------------

    PPRINT-EXIT-IF-LIST-EXHAUSTED                                       [Macro]

    PPRINT-EXIT-IF-LIST-EXHAUSTED tests whether or not the LIST passed to
    PPRINT-LOGICAL-BLOCK has been exhausted (see PPRINT-POP).  If this list has
    been reduced to NIL, PPRINT-EXIT-IF-LIST-EXHAUSTED terminates the execution
    of the immediately containing PPRINT-LOGICAL-BLOCK except for the printing
    of the suffix.  Otherwise PPRINT-EXIT-IF-LIST-EXHAUSTED returns NIL.  An
    error message is issued if PPRINT-EXIT-IF-LIST-EXHAUSTED is used anywhere
    other than syntactically nested within a call on PPRINT-LOGICAL-BLOCK.  It
    is undefined what happens if PPRINT-IF-LIST-EXHAUSTED is executed outside
    of the dynamic extent of this PPRINT-LOGICAL-BLOCK.

    PPRINT-POP                                                          [Macro]

    PPRINT-POP pops elements one at a time off the LIST passed to
    PPRINT-LOGICAL-BLOCK obeying *PRINT-LENGTH*, *PRINT-CIRCLE*, and
    *PRINT-SHARED*.  An error message is issued if it is used anywhere
    other than syntactically nested within a call on PPRINT-LOGICAL-BLOCK.
    It is undefined what happens if PPRINT-POP is executed outside of the
    dynamic extent of this PPRINT-LOGICAL-BLOCK.

    Each time PPRINT-POP is called, it pops the next value off the LIST
    passed to PPRINT-LOGICAL-BLOCK and returns it.  However, before doing
    this, it performs three tests.  If the remaining list is not a list
    (i.e., a cons or NIL), ". " is printed followed by the remaining list.
    (This makes it easier to write printing functions that are robust in
    the face of malformed arguments.)  If *PRINT-LENGTH* is NIL and
    PPRINT-POP has already been called *PRINT-LENGTH* times within the
    immediately containing logical block, "..." is printed.  (This makes
    it easy to write printing functions that properly handle
    *PRINT-LENGTH*.)  If *PRINT-CIRCLE* (and possibly *PRINT-SHARED*) is
    not NIL, and the remaining list is a circular (or shared) reference,
    then ". " is printed followed by an appropriate #n# marker.  (This
    catches instances of cdr circularity and sharing in lists.)

    If either of the three conditions above occurs, the indicated output is
    printed on the pretty printing stream created by the immediately containing
    PPRINT-LOGICAL-BLOCK and the execution of the immediately containing
    PPRINT-LOGICAL-BLOCK is terminated except for the printing of the suffix.

    If PPRINT-LOGICAL-BLOCK is given a LIST argument of NIL---because it is not
    processing a list---PPRINT-POP can still be used to obtain support for
    *PRINT-LENGTH* (see the example function PPRINT-VECTOR below).  In this
    situation, the first and third tests above are disabled and
    PPRINT-POP always returns NIL.

    PPRINT-INDENT relative-to n &OPTIONAL (stream *STANDARD-OUTPUT*) [Function]

    PPRINT-INDENT specifies the indentation to use in a logical block.  STREAM
    (which defaults to *STANDARD-OUTPUT*) follows the standard conventions for
    stream arguments to printing functions.  N specifies the indentation in
    ems.  If RELATIVE-TO is :BLOCK, the indentation is set to the horizontal
    position of the first character in the block plus N ems.  If RELATIVE-TO is
    :CURRENT, the indentation is set to the current output position plus N ems.
    (For robustness in the face of variable-width fonts, it is advisable to use
    :CURRENT with an N of zero whenever possible.)

    N can be negative; however, the total indentation cannot be moved left of
    the beginning of the line or left of the end of the rightmost per-line
    prefix.  Changes in indentation caused by PPRINT-INDENT do not take
    effect until after the next line break.  In addition, in miser mode all
    calls on PPRINT-INDENT are ignored, forcing the lines corresponding to the
    logical block to line up under the first character in the block.

    An error is signalled if a value other than :BLOCK or :CURRENT is supplied
    for RELATIVE-TO.  If STREAM is a pretty printing stream created by
    PPRINT-LOGICAL-BLOCK, PPRINT-INDENT sets the indentation in the innermost
    dynamically enclosing logical block.  Otherwise, PPRINT-INDENT has no
    effect.  The value NIL is always returned.

    PPRINT-TAB kind colnum colinc &OPTIONAL (stream *STANDARD-OUTPUT*) [function]

    PPRINT-TAB specifies tabbing as performed by the standard FORMAT directive
    ~T.  STREAM (which defaults to *STANDARD-OUTPUT*) follows the standard
    conventions for stream arguments to printing functions.  The arguments
    COLNUM and COLINC correspond to the two parameters to ~T and are in terms
    of ems.  The KIND argument specifies the style of tabbing.  It must be one
    of :LINE (tab as by ~T) :SECTION (tab as by ~T, but measuring horizontal
    positions relative to the start of the dynamically enclosing section),
    :LINE-RELATIVE (tab as by ~@T), or :SECTION-RELATIVE (tab as by ~@T, but
    measuring horizontal positions relative to the start of the dynamically
    enclosing section).  An error is signalled if any other value is supplied
    for KIND.  If STREAM is a pretty printing stream created by
    PPRINT-LOGICAL-BLOCK, tabbing is performed.  Otherwise, PPRINT-TAB has no
    effect.  The value NIL is always returned.

    PPRINT-FILL STREAM LIST &OPTIONAL (COLON? T) ATSIGN?                 [function]
    PPRINT-LINEAR STREAM LIST &OPTIONAL (COLON? T) ATSIGN?               [function]
    PPRINT-TABULAR STREAM LIST &OPTIONAL (COLON? T) ATSIGN? (TABSIZE 16) [function]

    These three functions specify particular ways of pretty printing lists.
    STREAM follows the standard conventions for stream arguments to printing
    functions.  Each function prints parentheses around the output if and only
    if COLON? (default T) is not NIL.  Each function ignores its ATSIGN?
    argument and returns NIL.  (These two arguments are included in this way so
    that these functions can be used via ~/.../ and as SET-PPRINT-DISPATCH
    functions as well as directly.)  Each function handles abbreviation and the
    detection of circularity and sharing correctly, and uses WRITE to print
    LIST when given a non-list argument.

    The function PPRINT-LINEAR prints a list either all on one line, or with
    each element on a separate line.  The function PPRINT-FILL prints a list
    with as many elements as possible on each line.  The function
    PPRINT-TABULAR is the same as PPRINT-FILL except that it prints the
    elements so that they line up in columns.  This function takes an
    additional argument TABSIZE (default 16) that specifies the column
    spacing in ems.

    ---

    As an example of the interaction of logical blocks, conditional newlines,
    and indentation, consider the function SIMPLE-PPRINT-DEFUN below.  This
    function prints out lists whose cars are DEFUN in the standard way assuming
    that the list has exactly length 4.

    (defun simple-pprint-defun (*standard-output* list)
      (pprint-logical-block (*standard-output* list :prefix "(" :suffix ")")
        (write (first list))
        (write-char #\space)
        (pprint-newline :miser)
        (pprint-indent :current 0)
        (write (second list))
        (write-char #\space)
        (pprint-newline :fill)
        (write (third list))
        (pprint-indent :block 1)
        (write-char #\space)
        (pprint-newline :linear)
        (write (fourth list))))

    Suppose that one evaluates the following:

    (simple-pprint-defun *standard-output* '(defun prod (x y) (* x y)))

    If the line width available is greater than or equal to 26, then all of the
    output appears on one line.  If the line width available is reduced to 25,
    a line break is inserted at the linear-style conditional newline before the
    expression (* X Y), producing the output shown.  The (PPRINT-INDENT :BLOCK 1)
    causes (* X Y) to be printed at a relative indentation of 1 in the logical block.

    (DEFUN PROD (X Y)
      (* X Y))

    If the line width available is 15, a line break is also inserted at the
    fill style conditional newline before the argument list.  The call on
    (PPRINT-INDENT :CURRENT 0) causes the argument list to line up under the
    function name.

    (DEFUN PROD
           (X Y)
      (* X Y))

    If *PRINT-MISER-WIDTH* were greater than or equal to 14, the example output
    above would have been as follows, because all indentation changes are
    ignored in miser mode and line breaks are inserted at miser-style
    conditional newlines.

    (DEFUN
     PROD
     (X Y)
     (* X Y))

    ---

    As an example of a per-line prefix, consider that evaluating the following
    produces the output shown with a line width of 20 and *PRINT-MISER-WIDTH*
    of NIL.

    (pprint-logical-block (*standard-output* nil :per-line-prefix ";;; ")
      (simple-pprint-defun *standard-output* '(defun prod (x y) (* x y))))

    ;;; (DEFUN PROD
    ;;;        (X Y)
    ;;;   (* X Y))

    ---

    As a more complex (and realistic) example, consider the function PPRINT-LET
    below.  This specifies how to print a LET in the standard style.  It is more
    complex than the example above, because it has to deal with nested structure.
    Also, unlike the example above it contains complete code to readably print any
    possible list that begins with the symbol LET.  The outermost
    PPRINT-LOGICAL-BLOCK handles the printing of the input list as a whole and
    specifies that parentheses should be printed in the output.  The second
    PPRINT-LOGICAL-BLOCK handles the list of binding pairs.  Each pair in the list
    is itself printed by the innermost PPRINT-LOGICAL-BLOCK.  (A LOOP is used
    instead of merely decomposing the pair into two elements so that readable
    output will be produced no matter whether the list corresponding to the pair
    has one element, two elements, or (being malformed) has more than two
    elements.)   A space and a fill-style conditional newline are placed after
    each pair except the last.  The loop at the end of the topmost
    PPRINT-LOGICAL-BLOCK prints out the forms in the body of the LET separated by
    spaces and linear-style conditional newlines.

    (defun pprint-let (*standard-output* list)
      (pprint-logical-block (nil list :prefix "(" :suffix ")")
        (write (pprint-pop))
        (pprint-exit-if-list-exhausted)
        (write-char #\space)
        (pprint-logical-block (nil (pprint-pop) :prefix "(" :suffix ")")
          (pprint-exit-if-list-exhausted)
          (loop (pprint-logical-block (nil (pprint-pop) :prefix "(" :suffix ")")
    	      (pprint-exit-if-list-exhausted)
    	      (loop (write (pprint-pop))
    		    (pprint-exit-if-list-exhausted)
    		    (write-char #\space)
    		    (pprint-newline :linear)))
    	    (pprint-exit-if-list-exhausted)
    	    (write-char #\space)
    	    (pprint-newline :fill)))
        (pprint-indent :block 1)
        (loop (pprint-exit-if-list-exhausted)
    	  (write-char #\space)
    	  (pprint-newline :linear)
    	  (write (pprint-pop)))))

    Suppose that one evaluates the following with *PRINT-LEVEL* 4, and
    *PRINT-CIRCLE* T.

    (pprint-let *standard-output*
    	    '#1=(let (x (*print-length* (f (g 3)))
    		      (z . 2) (k (car y)))
    		  (setq x (sqrt z)) #1#))

    If the line length is greater than or equal to 77, the output produced
    appears on one line.  However, if the line length is 76, line breaks are
    inserted at the linear-style conditional newlines separating the forms in
    the body and the output below is produced.  Note that, the degenerate
    binding pair X is printed readably even though it fails to be a list; a
    depth abbreviation marker is printed in place of (G 3); the binding pair
    (Z . 2) is printed readably even though it is not a proper list; and
    appropriate circularity markers are printed.

    #1=(LET (X (*PRINT-LENGTH* (F #)) (Z . 2) (K (CAR Y)))
         (SETQ X (SQRT Z))
         #1#)

    If the line length is reduced to 35, a line break is inserted at one of the
    fill-style conditional newlines separating the binding pairs.

    #1=(LET (X (*PRINT-PRETTY* (F #))
             (Z . 2) (K (CAR Y)))
         (SETQ X (SQRT Z))
         #1#)

    Suppose that the line length is further reduced to 22 and *PRINT-LENGTH* is
    set to 3. In this situation, line breaks are inserted after both the first
    and second binding pairs.  In addition, the second binding pair is itself
    broken across two lines.  Clause (b) of the description of fill-style
    conditional newlines prevents the binding pair (Z . 2) from being printed
    at the end of the third line.  Note that the length abbreviation hides the
    circularity from view and therefore the printing of circularity markers
    disappears.

    (LET (X
          (*PRINT-LENGTH*
           (F #))
          (Z . 2) ...)
      (SETQ X (SQRT Z))
      ...)

    ---

    The function PPRINT-TABULAR could be defined as follows.

    (defun pprint-tabular (s list &optional (colon? T) atsign? (tabsize nil))
      (declare (ignore atsign?))
      (when (null tabsize) (setq tabsize 16))
      (pprint-logical-block (s list :prefix (if colon? "(" "")
    			        :suffix (if colon? ")" ""))
        (pprint-exit-if-list-exhausted)
        (loop (write (pprint-pop) :stream s)
    	  (pprint-exit-if-list-exhausted)
    	  (write-char #\space s)
    	  (pprint-tab :section-relative 0 tabsize s)
    	  (pprint-newline :fill s))))

    Evaluating the following with a line length of 25 produces the output shown.

    (princ "Roads ")
    (pprint-tabular *standard-output* '(elm main maple center) nil nil 8)

    Roads ELM     MAIN
          MAPLE   CENTER

    ---

    The function below prints a vector using #(...) notation.

    (defun pprint-vector (*standard-output* v)
      (pprint-logical-block (nil nil :prefix "#(" :suffix ")")
        (let ((end (length v)) (i 0))
          (when (plusp end)
    	(loop (pprint-pop)
    	      (write (aref v i))
    	      (if (= (incf i) end) (return nil))
    	      (write-char #\space)
    	      (pprint-newline :fill))))))

    Evaluating the following with a line length of 15 produces the output shown.

    (pprint-vector *standard-output* '#(12 34 567 8 9012 34 567 89 0 1 23))

    #(12 34 567 8
      9012 34 567
      89 0 1 23)

                         Format Directive Interface

    The primary interface to operations for dynamically determining the
    arrangement of output is provided through the functions above.  However, an
    additional interface is provided via a set of new format directives.
    This is done, because as shown by the examples in this section and the
    next, FORMAT strings are typically a much more compact way to specify
    pretty printing.  In addition, without such an interface, one would have to
    abandon the use of FORMAT when interacting with the pretty printer.

    ~W                                                        [format directive]

    WRITE -- An arg, any Lisp object, is printed obeying every printer control
    variable (as by WRITE).  In addition, ~W interacts correctly with depth
    abbreviation, by not resetting the depth counter to zero.  ~W does not
    accept parameters.  If given the colon modifier, ~W binds *PRINT-PRETTY* to
    T.  If given the atsign modifier, ~W binds *PRINT-LEVEL* and *PRINT-LENGTH*
    to NIL.

    ~W provides automatic support for the detection of circularity and
    sharing.  If *PRINT-CIRCLE* (and possibly *PRINT-SHARED*) is not NIL
    and ~W is applied to an argument that is a circular (or shared)
    reference, an appropriate #n# marker is inserted in the output instead
    of printing the argument.

    ~_                                                        [format directive]

    CONDITIONAL-NEWLINE -- Without any modifiers, ~_ is the same as
    (PPRINT-NEWLINE :LINEAR).  ~@_ is the same as (PPRINT-NEWLINE :MISER).
    ~:_ is the same as (PPRINT-NEWLINE :FILL).  ~:@_ is the same as
    (PPRINT-NEWLINE :MANDATORY).

    ~<...~:>                                                  [format directive]

    LOGICAL BLOCK -- If ~:> is used to terminate a ~<...~>, the directive
    is equivalent to a call on PPRINT-LOGICAL-BLOCK.  The FORMAT argument
    corresponding to the ~<...~:> directive is treated in the same way as
    the LIST argument to PPRINT-LOGICAL-BLOCK, thereby providing automatic
    support for non-list arguments and the detection of circularity,
    sharing, and depth abbreviation.  The portion of the FORMAT control
    string nested within the ~<...~:> specifies the :PREFIX (or
    :PER-LINE-PREFIX), :suffix}, and body of the PPRINT-LOGICAL-BLOCK.

    The FORMAT string portion enclosed by ~<...~:> can be divided into
    segments ~<prefix~;body~;suffix~:> by ~; directives.  If the first
    section is terminated by ~@;, it specifies a per-line prefix rather
    than a simple prefix.  The prefix and suffix cannot contain FORMAT
    directives.  An error is signalled if either the prefix or suffix
    fails to be a constant string or if the enclosed portion is divided
    into more than three segments.

    If the enclosed portion is divided into only two segments, the suffix
    defaults to the null string.  If the enclosed portion consists of only
    a single segment, both the prefix and the suffix default to the null
    string.  If the colon modifier is used (i.e., ~:<...~:>), the prefix
    and suffix default to "(" and ")" (respectively) instead of the null
    string.

    The body segment can be any arbitrary FORMAT control string.  This
    FORMAT control string is applied to the elements of the list
    corresponding to the ~<...~:> directive as a whole.  Elements are
    extracted from this list using PPRINT-POP, thereby providing automatic
    support for malformed lists, and the detection of circularity,
    sharing, and length abbreviation.  Within the body segment, ~^ acts
    like PPRINT-EXIT-IF-LIST-EXHAUSTED.

    ~<...~:> supports a feature not supported by PPRINT-LOGICAL-BLOCK.  If
    ~:@> is used to terminate the directive (i.e., ~<...~:@>), then a
    fill-style conditional newline is automatically inserted after each
    group of blanks immediately contained in the body (except for blanks
    after a ~<newline> directive).  This makes it easy to achieve the
    equivalent of paragraph filling.

    If the atsign modifier is used with ~<...~:>, the entire remaining argument
    list is passed to the directive as its argument.  All of the remaining
    arguments are always consumed by ~@<...~:>, even if they are not all used
    by the FORMAT string nested in the directive.  Other than the difference in
    its argument, ~@<...~:> is exactly the same as ~<...~:> except that
    circularity detection is not applied if ~@<...~:> is encountered at top
    level in a {\cd format} string.  This ensures that circularity detection is
    applied only to data lists, not to {\cd format} argument lists.

    " . #n#" is printed if circularity or sharing has
    to be indicated for its argument as a whole.

    To a considerable extent, the basic form of the directive ~<...~> is
    incompatible with the dynamic control of the arrangement of output by
    ~W, ~_, ~<...~:>, ~I, and ~:T.  As a result, an error is signalled if
    any of these directives is nested within ~<...~>.  Beyond this, an
    error is also signalled if the ~<...~:;...~> form of ~<...~> is used
    in the same FORMAT string with ~W, ~_, ~<...~:>, ~I, or ~:T.

    ~I                                                        [format directive]

    INDENT -- ~nI is the same as (PPRINT-INDENT :BLOCK N).
    ~n:I is the same as (PPRINT-INDENT :CURRENT N).  In both cases, N defaults
    to zero, if it is omitted.

    ~:T                                                       [format directive]

    TABULATE -- If the colon modifier is used with the ~T directive, the
    tabbing computation is done relative to the horizontal position where the
    section immediately containing the directive begins, rather than with
    respect to a horizontal position of zero.  The numerical parameters are
    both interpreted as being in units of ems and both default to 1.
    ~n,m:T is the same as (PPRINT-TAB :SECTION N M).
    ~n,m:@T is the same as (PPRINT-TAB :SECTION-RELATIVE N M).

    ~/name/                                                   [format directive]

    CALL FUNCTION -- User defined functions can be called from within a FORMAT
    string by using the directive ~/name/.  The colon modifier, the atsign
    modifier, and arbitrarily many parameters can be specified with the ~/name/
    directive.  NAME can be any arbitrary string that does not contain a "/".
    All of the characters in NAME are treated as if they were upper case.  If
    NAME contains a ":" or "::", then everything up to but not including the
    first ":" or "::" is taken to be a string that names a package.  Everything
    after the first ":" or "::" (if any) is taken to be a string that names a
    symbol.  The function corresponding to a ~/name/ directive is obtained by
    looking up the symbol that has the indicated name in the indicated package.
    If NAME does not contain a ":" or "::", then the whole name string is
    looked up in the USER package.

    When a ~/name/ directive is encountered, the indicated function is called
    with four or more arguments.  The first four arguments are: the output
    stream, the FORMAT argument corresponding to the directive, the value T if
    the colon modifier was used (NIL otherwise), and the value T if the atsign
    modifier was used (NIL otherwise).  The remaining arguments consist of any
    parameters specified with the directive.  The function should print the
    argument appropriately.  Any values returned by the function are ignored.

    The three functions PPRINT-LINEAR, PPRINT-FILL, and PPRINT-TABULAR are
    specifically designed so that they can be called by ~/.../ (i.e.,
    ~/PPRINT-LINEAR/, ~/PPRINT-FILL/, and ~/PPRINT-TABULAR/).  In particular
    they take colon and atsign arguments.

    ---

    As examples of the convenience of specifying pretty printing with FORMAT
    strings, consider that the first two functions used as examples in the last
    section can be compactly defined as follows.  The function PPRINT-VECTOR
    cannot be defined using FORMAT, because the data structure it traverses is
    not a list.  The function PPRINT-TABULAR is inconvenient to define using
    FORMAT, because of the need to pass its TABSIZE argument through to a ~:T
    directive nested within an iteration over a list.

    (defun simple-pprint-defun (*standard-output* list)
      (format T "~:<~W ~@_~:I~W ~:_~W~1I ~_~W~:>" list))

    (defun pprint-let (*standard-output* list)
      (format T "~:<~W~^ ~:<~@{~:<~@{~W~^ ~_~}~:>~^ ~:_~}~:>~1I~@{~^ ~_~W~}~:>" list))

    		   Compiling Format Control Strings

    The control strings used by FORMAT are essentially programs that perform
    printing.  The macro FORMATTER provides the efficiency of using a compiled
    function for printing without losing the compactness of FORMAT control
    strings.

    FORMATTER control-string                                           [macro]

    CONTROL-STRING must be a literal string.  An error is signalled if
    CONTROL-STRING is not a valid FORMAT control string.  The macro FORMATTER
    expands into an expression of the form (FUNCTION (LAMBDA (STREAM &REST
    ARGS) ...))  that does the printing specified by CONTROL-STRING.  The
    LAMBDA created accepts an output stream as its first argument and zero or
    more data values as its remaining arguments.  The value returned by the
    LAMBDA is the tail (if any) of the data values that are not printed out by
    CONTROL-STRING.  (E.g., if the CONTROL-STRING is "~A~A" the CDDR (if any)
    of the data values is returned.)

    For instance: (formatter "~%~2@{~S, ~}") is equivalent to

    #'(lambda (stream &rest args)
        (terpri stream)
        (dotimes (n 2)
          (if (null args) (return nil))
          (prin1 (pop args) stream)
          (write-string ", " stream))
        args)

    In support of the above, FORMAT is extended so that it accepts functions as
    its second argument as well as strings.  When a function is provided, it
    must be a function of the form created by FORMATTER.  The function is called
    with the appropriate output stream as its first argument and the data
    arguments to FORMAT as its remaining arguments.  The function should
    perform whatever output is necessary and return the unused tail of the
    arguments (if any).  The directives ~? and ~{~} with no body are also
    extended so that they accept functions as well as control strings.
    Every other standard function that takes a FORMAT string as an argument
    (e.g., ERROR and WARN) are also extended so that they can accept functions
    of the form above instead.

    			Pretty Print 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.

    Pprint dispatch tables are mappings 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 from
    *PRINT-PPRINT-DISPATCH* that is associated with a type specifier that
    matches the object.

    COPY-PPRINT-DISPATCH &optional (table *PRINT-PPRINT-DISPATCH*)    [function]

    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 *PRINT-PPRINT-DISPATCH*)   [function]

    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-specifier function                        [function]
                        &optional (priority 0) (table *PRINT-PPRINT-DISPATCH*)

    This puts an entry into a pprint dispatch table and returns NIL.
    TYPE-SPECIFIER 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-SPECIFIER.  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.  FUNCTION must accept two arguments: the
    stream to send output to and the object to be printed.  FUNCTION should
    pretty print the object on the stream.  FUNCTION can assume that object
    satisfies TYPE-SPECIFIER.  Function must obey *PRINT-READABLY* (see issue
    DATA-IO).  Any values returned by FUNCTION are ignored.

    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 signalled if priority fails
    to be a non-complex number.

    TABLE (which defaults to *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-SPECIFIER 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 constructs of the form

               (CONS car-type cdr-type)

    This signifies that the corresponding object must be a cons cell whose car
    matches the type specifier CAR-TYPE and whose cdr matches 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
    have the property that they are less than every priority that can be
    specified using SET-PPRINT-DISPATCH.  The benefit of this is that it
    guarantees that any pretty printing functions users specify will override
    everything in the initial value of *PRINT-PPRINT-DISPATCH*.

    ----------------------------------------------------------------------

    Implementation note:  The restriction above is very useful to users
    without actually limiting what Common Lisp implementors can do.  It is
    possible for implementors to set up any kind of pretty printing they
    desire using the range of priorities available to them.

    ----------------------------------------------------------------------

    Consider the following examples.  The first form restores
    *PRINT-PPRINT-DISPATCH* to its initial value.  The next two forms then set
    up a special way to pretty print ratios.  Note that the more specific type
    specifier has to be associated with a higher priority.

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

    (set-pprint-dispatch 'ratio
      #'(lambda (s obj)
          (format s "#.(/ ~W ~W)" (numerator obj) (denominator obj))))

    (set-pprint-dispatch '(and ratio (satisfies minusp))
      #'(lambda (s obj)
          (format s "#.(- (/ ~W ~W))" (- (numerator obj)) (denominator obj)))
      5)

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

    The following two forms illustrate the definition of pretty printing
    functions for 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
    all defined with optional COLON? and ATSIGN? arguments so that they can be
    used as pprint dispatch functions as well as ~/.../ functions.

    (set-pprint-dispatch '(cons (not (and symbol (satisfies fboundp))))
                         #'pprint-fill -5)

    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)
          (funcall (formatter "~@<#<~;~W and ~2I~_~/pprint-fill/~;>~:>")
    	       s (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)

    (PRINCIPAL-FAMILY
     #<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.

Copyright 1996-2005, LispWorks Ltd. All rights reserved.