Apropos

22.1 The Lisp Printer

22.1.1 Overview of The Lisp Printer

Common Lisp provides a representation of most objects in the form of printed text called the printed representation. Functions such as print take an object and send the characters of its printed representation to a stream. The collection of routines that does this is known as the (Common Lisp) printer.

Reading a printed representation typically produces an object that is equal to the originally printed object.

22.1.1.1 Multiple Possible Textual Representations

Most objects have more than one possible textual representation. For example, the positive integer with a magnitude of twenty-seven can be textually expressed in any of these ways:

27    27.    #o33    #x1B    #b11011    #.(* 3 3 3)    81/3

A list containing the two symbols A and B can also be textually expressed in a variety of ways:

 (A B)    (a b)    (  a  b )    (\A |B|) 
(|\A| 
  B 
)

In general, from the point of view of the Lisp reader, wherever whitespace is permissible in a textual representation, any number of spaces and newlines can appear in standard syntax.

When a function such as print produces a printed representation, it must choose from among many possible textual representations. In most cases, it chooses a program readable representation, but in certain cases it might use a more compact notation that is not program-readable.

A number of option variables, called printer control variables, are provided to permit control of individual aspects of the printed representation of objects. Figure 22–1 shows the standardized printer control variables; there might also be implementation-defined printer control variables.

In addition to the printer control variables, the following additional defined names relate to or affect the behavior of the Lisp printer:

Figure 22–2. Additional Influences on the Lisp printer.
22.1.1.1.1 Printer Escaping

The variable *print-escape* controls whether the Lisp printer tries to produce notations such as escape characters and package prefixes.

The variable *print-readably* can be used to override many of the individual aspects controlled by the other printer control variables when program-readable output is especially important.

One of the many effects of making the value of *print-readably* be true is that the Lisp printer behaves as if *print-escape* were also true. For notational convenience, we say that if the value of either *print-readably* or *print-escape* is true, then printer escaping is “enabled”; and we say that if the values of both *print-readably* and *print-escape* are false, then printer escaping is “disabled”.

22.1.2 Printer Dispatching

The Lisp printer makes its determination of how to print an object as follows:

If the value of *print-pretty* is true, printing is controlled by the current pprint dispatch table; see Section 22.2.1.4 (Pretty Print Dispatch Tables).

Otherwise (if the value of *print-pretty* is false), the object’s print-object method is used; see Section 22.1.3 (Default Print-Object Methods).

22.1.3 Default Print-Object Methods

This section describes the default behavior of print-object methods for the standardized types.

22.1.3.1 Printing Numbers

22.1.3.1.1 Printing Integers

Integers are printed in the radix specified by the current output base in positional notation, most significant digit first. If appropriate, a radix specifier can be printed; see *print-radix*. If an integer is negative, a minus sign is printed and then the absolute value of the integer is printed. The integer zero is represented by the single digit 0 and never has a sign. A decimal point might be printed, depending on the value of *print-radix*.

For related information about the syntax of an integer, see Section 2.3.2.1.1 (Syntax of an Integer).

22.1.3.1.2 Printing Ratios

Ratios are printed as follows: the absolute value of the numerator is printed, as for an integer; then a /; then the denominator. The numerator and denominator are both printed in the radix specified by the current output base; they are obtained as if by numerator and denominator, and so ratios are printed in reduced form (lowest terms). If appropriate, a radix specifier can be printed; see *print-radix*. If the ratio is negative, a minus sign is printed before the numerator.

For related information about the syntax of a ratio, see Section 2.3.2.1.2 (Syntax of a Ratio).

22.1.3.1.3 Printing Floats

If the magnitude of the float is either zero or between 10-3 (inclusive) and 10sup7 (exclusive), it is printed as the integer part of the number, then a decimal point, followed by the fractional part of the number; there is always at least one digit on each side of the decimal point. If the sign of the number (as determined by float-sign) is negative, then a minus sign is printed before the number. If the format of the number does not match that specified by *read-default-float-format*, then the exponent marker for that format and the digit 0 are also printed. For example, the base of the natural logarithms as a short float might be printed as 2.71828S0.

For non-zero magnitudes outside of the range 10-3 to 10sup7, a float is printed in computerized scientific notation. The representation of the number is scaled to be between 1 (inclusive) and 10 (exclusive) and then printed, with one digit before the decimal point and at least one digit after the decimal point. Next the exponent marker for the format is printed, except that if the format of the number matches that specified by *read-default-float-format*, then the exponent marker E is used. Finally, the power of ten by which the fraction must be multiplied to equal the original number is printed as a decimal integer. For example, Avogadro’s number as a short float is printed as 6.02S23.

For related information about the syntax of a float, see Section 2.3.2.2 (Syntax of a Float).

22.1.3.1.4 Printing Complexes

A complex is printed as #C, an open parenthesis, the printed representation of its real part, a space, the printed representation of its imaginary part, and finally a close parenthesis.

For related information about the syntax of a complex, see Section 2.3.2.3 (Syntax of a Complex) and Section 2.4.8.11 (Sharpsign C).

22.1.3.1.5 Note about Printing Numbers

The printed representation of a number must not contain escape characters; see Section 2.3.1.1.1 (Escape Characters and Potential Numbers).

22.1.3.2 Printing Characters

When printer escaping is disabled, a character prints as itself; it is sent directly to the output stream. When printer escaping is enabled, then #\ syntax is used.

When the printer types out the name of a character, it uses the same table as the #\ reader macro would use; therefore any character name that is typed out is acceptable as input (in that implementation). If a non-graphic character has a standardized name5, that name is preferred over non-standard names for printing in #\ notation. For the graphic standard characters, the character itself is always used for printing in #\ notation — even if the character also has a name5.

For details about the #\ reader macro, see Section 2.4.8.1 (Sharpsign Backslash).

22.1.3.3 Printing Symbols

When printer escaping is disabled, only the characters of the symbol’s name are output (but the case in which to print characters in the name is controlled by *print-case*; see Section 22.1.3.3.2 (Effect of Readtable Case on the Lisp Printer)).

The remainder of this section applies only when printer escaping is enabled.

When printing a symbol, the printer inserts enough single escape and/or multiple escape characters (backslashes and/or vertical-bars) so that if read were called with the same *readtable* and with *read-base* bound to the current output base, it would return the same symbol (if it is not apparently uninterned) or an uninterned symbol with the same print name (otherwise).

For example, if the value of *print-base* were 16 when printing the symbol face, it would have to be printed as \FACE or \Face or |FACE|, because the token face would be read as a hexadecimal number (decimal value 64206) if the value of *read-base* were 16.

For additional restrictions concerning characters with nonstandard syntax types in the current readtable, see the variable *print-readably*

For information about how the Lisp reader parses symbols, see Section 2.3.4 (Symbols as Tokens) and Section 2.4.8.5 (Sharpsign Colon).

nil might be printed as () when *print-pretty* is true and printer escaping is enabled.

22.1.3.3.1 Package Prefixes for Symbols

Package prefixes are printed if necessary. The rules for package prefixes are as follows. When the symbol is printed, if it is in the KEYWORD package, then it is printed with a preceding colon; otherwise, if it is accessible in the current package, it is printed without any package prefix; otherwise, it is printed with a package prefix.

A symbol that is apparently uninterned is printed preceded by “#:” if *print-gensym* is true and printer escaping is enabled; if *print-gensym* is false or printer escaping is disabled, then the symbol is printed without a prefix, as if it were in the current package.

Because the #: syntax does not intern the following symbol, it is necessary to use circular-list syntax if *print-circle* is true and the same uninterned symbol appears several times in an expression to be printed. For example, the result of

(let ((x (make-symbol "FOO"))) (list x x))

would be printed as (#:foo #:foo) if *print-circle* were false, but as (#1=#:foo #1#) if *print-circle* were true.

A summary of the preceding package prefix rules follows:

foo:bar

foo:bar is printed when symbol bar is external in its home package foo and is not accessible in the current package.

foo::bar

foo::bar is printed when bar is internal in its home package foo and is not accessible in the current package.

:bar

:bar is printed when the home package of bar is the KEYWORD package.

#:bar

#:bar is printed when bar is apparently uninterned, even in the pathological case that bar has no home package but is nevertheless somehow accessible in the current package.

22.1.3.3.2 Effect of Readtable Case on the Lisp Printer

When printer escaping is disabled, or the characters under consideration are not already quoted specifically by single escape or multiple escape syntax, the readtable case of the current readtable affects the way the Lisp printer writes symbols in the following ways:

:upcase

When the readtable case is :upcase, uppercase characters are printed in the case specified by *print-case*, and lowercase characters are printed in their own case.

:downcase

When the readtable case is :downcase, uppercase characters are printed in their own case, and lowercase characters are printed in the case specified by *print-case*.

:preserve

When the readtable case is :preserve, all alphabetic characters are printed in their own case.

:invert

When the readtable case is :invert, the case of all alphabetic characters in single case symbol names is inverted. Mixed-case symbol names are printed as is.

The rules for escaping alphabetic characters in symbol names are affected by the readtable-case if printer escaping is enabled. Alphabetic characters are escaped as follows:

:upcase

When the readtable case is :upcase, all lowercase characters must be escaped.

:downcase

When the readtable case is :downcase, all uppercase characters must be escaped.

:preserve

When the readtable case is :preserve, no alphabetic characters need be escaped.

:invert

When the readtable case is :invert, no alphabetic characters need be escaped.

22.1.3.3.2.1 Examples of Effect of Readtable Case on the Lisp Printer
(defun test-readtable-case-printing () 
  (let ((*readtable* (copy-readtable nil)) 
        (*print-case* *print-case*)) 
    (format t "READTABLE-CASE *PRINT-CASE*  Symbol-name  Output~ 
             ~%--------------------------------------------------~ 
             ~%") 
    (dolist (readtable-case '(:upcase :downcase :preserve :invert)) 
      (setf (readtable-case *readtable*) readtable-case) 
      (dolist (print-case '(:upcase :downcase :capitalize)) 
        (dolist (symbol '(|ZEBRA| |Zebra| |zebra|)) 
          (setq *print-case* print-case) 
          (format t "~&:~A~15T:~A~29T~A~42T~A" 
                  (string-upcase readtable-case) 
                  (string-upcase print-case) 
                  (symbol-name symbol) 
                  (prin1-to-string symbol)))))))

The output from (test-readtable-case-printing) should be as follows:

READTABLE-CASE *PRINT-CASE*  Symbol-name  Output 
-------------------------------------------------- 
:UPCASE        :UPCASE       ZEBRA        ZEBRA 
:UPCASE        :UPCASE       Zebra        |Zebra| 
:UPCASE        :UPCASE       zebra        |zebra| 
:UPCASE        :DOWNCASE     ZEBRA        zebra 
:UPCASE        :DOWNCASE     Zebra        |Zebra| 
:UPCASE        :DOWNCASE     zebra        |zebra| 
:UPCASE        :CAPITALIZE   ZEBRA        Zebra 
:UPCASE        :CAPITALIZE   Zebra        |Zebra| 
:UPCASE        :CAPITALIZE   zebra        |zebra| 
:DOWNCASE      :UPCASE       ZEBRA        |ZEBRA| 
:DOWNCASE      :UPCASE       Zebra        |Zebra| 
:DOWNCASE      :UPCASE       zebra        ZEBRA 
:DOWNCASE      :DOWNCASE     ZEBRA        |ZEBRA| 
:DOWNCASE      :DOWNCASE     Zebra        |Zebra| 
:DOWNCASE      :DOWNCASE     zebra        zebra 
:DOWNCASE      :CAPITALIZE   ZEBRA        |ZEBRA| 
:DOWNCASE      :CAPITALIZE   Zebra        |Zebra| 
:DOWNCASE      :CAPITALIZE   zebra        Zebra 
:PRESERVE      :UPCASE       ZEBRA        ZEBRA 
:PRESERVE      :UPCASE       Zebra        Zebra 
:PRESERVE      :UPCASE       zebra        zebra 
:PRESERVE      :DOWNCASE     ZEBRA        ZEBRA 
:PRESERVE      :DOWNCASE     Zebra        Zebra 
:PRESERVE      :DOWNCASE     zebra        zebra 
:PRESERVE      :CAPITALIZE   ZEBRA        ZEBRA 
:PRESERVE      :CAPITALIZE   Zebra        Zebra 
:PRESERVE      :CAPITALIZE   zebra        zebra 
:INVERT        :UPCASE       ZEBRA        zebra 
:INVERT        :UPCASE       Zebra        Zebra 
:INVERT        :UPCASE       zebra        ZEBRA 
:INVERT        :DOWNCASE     ZEBRA        zebra 
:INVERT        :DOWNCASE     Zebra        Zebra 
:INVERT        :DOWNCASE     zebra        ZEBRA 
:INVERT        :CAPITALIZE   ZEBRA        zebra 
:INVERT        :CAPITALIZE   Zebra        Zebra 
:INVERT        :CAPITALIZE   zebra        ZEBRA

22.1.3.4 Printing Strings

The characters of the string are output in order. If printer escaping is enabled, a double-quote is output before and after, and all double-quotes and single escapes are preceded by backslash. The printing of strings is not affected by *print-array*. Only the active elements of the string are printed.

For information on how the Lisp reader parses strings, see Section 2.4.5 (Double-Quote).

22.1.3.5 Printing Lists and Conses

Wherever possible, list notation is preferred over dot notation. Therefore the following algorithm is used to print a cons x:

  1. A left-parenthesis is printed.

  2. The car of x is printed.

  3. If the cdr of x is itself a cons, it is made to be the current cons (i.e., x becomes that cons), a space is printed, and step 2 is re-entered.

  4. If the cdr of x is not null, a space, a dot, a space, and the cdr of x are printed.

  5. A right-parenthesis is printed.

Actually, the above algorithm is only used when *print-pretty* is false. When *print-pretty* is true (or when pprint is used), additional whitespace1 may replace the use of a single space, and a more elaborate algorithm with similar goals but more presentational flexibility is used; see Section 22.1.2 (Printer Dispatching).

Although the two expressions below are equivalent, and the reader accepts either one and produces the same cons, the printer always prints such a cons in the second form.

(a . (b . ((c . (d . nil)) . (e . nil)))) 
(a b (c d) e)

The printing of conses is affected by *print-level*, *print-length*, and *print-circle*.

Following are examples of printed representations of lists:

(a . b)     ;A dotted pair of a and b 
(a.b)       ;A list of one element, the symbol named a.b 
(a. b)      ;A list of two elements a. and b 
(a .b)      ;A list of two elements a and .b 
(a b . c)   ;A dotted list of a and b with c at the end; two conses 
.iot        ;The symbol whose name is .iot 
(. b)       ;Invalid -- an error is signaled if an attempt is made to read 
            ;this syntax. 
(a .)       ;Invalid -- an error is signaled. 
(a .. b)    ;Invalid -- an error is signaled. 
(a . . b)   ;Invalid -- an error is signaled. 
(a b c ...) ;Invalid -- an error is signaled. 
(a \. b)    ;A list of three elements a, ., and b 
(a |.| b)   ;A list of three elements a, ., and b 
(a \... b)  ;A list of three elements a, ..., and b 
(a |...| b) ;A list of three elements a, ..., and b

For information on how the Lisp reader parses lists and conses, see Section 2.4.1 (Left-Parenthesis).

22.1.3.6 Printing Bit Vectors

A bit vector is printed as #* followed by the bits of the bit vector in order. If *print-array* is false, then the bit vector is printed in a format (using #<) that is concise but not readable. Only the active elements of the bit vector are printed.

For information on Lisp reader parsing of bit vectors, see Section 2.4.8.4 (Sharpsign Asterisk).

22.1.3.7 Printing Other Vectors

If *print-array* is true and *print-readably* is false, any vector other than a string or bit vector is printed using general-vector syntax; this means that information about specialized vector representations does not appear. The printed representation of a zero-length vector is #(). The printed representation of a non-zero-length vector begins with #(. Following that, the first element of the vector is printed. If there are any other elements, they are printed in turn, with each such additional element preceded by a space if *print-pretty* is false, or whitespace1 if *print-pretty* is true. A right-parenthesis after the last element terminates the printed representation of the vector. The printing of vectors is affected by *print-level* and *print-length*. If the vector has a fill pointer, then only those elements below the fill pointer are printed.

If both *print-array* and *print-readably* are false, the vector is not printed as described above, but in a format (using #<) that is concise but not readable.

If *print-readably* is true, the vector prints in an implementation-defined manner; see the variable *print-readably*.

For information on how the Lisp reader parses these “other vectors,” see Section 2.4.8.3 (Sharpsign Left-Parenthesis).

22.1.3.8 Printing Other Arrays

If *print-array* is true and *print-readably* is false, any array other than a vector is printed using #nA format. Let n be the rank of the array. Then # is printed, then n as a decimal integer, then A, then n open parentheses. Next the elements are scanned in row-major order, using write on each element, and separating elements from each other with whitespace1. The array’s dimensions are numbered 0 to n-1 from left to right, and are enumerated with the rightmost index changing fastest. Every time the index for dimension j is incremented, the following actions are taken:

  • If j < n-1, then a close parenthesis is printed.

  • If incrementing the index for dimension j caused it to equal dimension j, that index is reset to zero and the index for dimension j-1 is incremented (thereby performing these three steps recursively), unless j=0, in which case the entire algorithm is terminated. If incrementing the index for dimension j did not cause it to equal dimension j, then a space is printed.

  • If j < n-1, then an open parenthesis is printed.

This causes the contents to be printed in a format suitable for :initial-contents to make-array. The lists effectively printed by this procedure are subject to truncation by *print-level* and *print-length*.

If the array is of a specialized type, containing bits or characters, then the innermost lists generated by the algorithm given above can instead be printed using bit-vector or string syntax, provided that these innermost lists would not be subject to truncation by *print-length*.

If both *print-array* and *print-readably* are false, then the array is printed in a format (using #<) that is concise but not readable.

If *print-readably* is true, the array prints in an implementation-defined manner; see the variable *print-readably*. In particular, this may be important for arrays having some dimension 0.

For information on how the Lisp reader parses these “other arrays,” see Section 2.4.8.12 (Sharpsign A).

22.1.3.9 Examples of Printing Arrays

 (let ((a (make-array '(3 3))) 
       (*print-pretty* t) 
       (*print-array* t)) 
   (dotimes (i 3) (dotimes (j 3) (setf (aref a i j) (format nil "<~D,~D>" i j)))) 
   (print a) 
   (print (make-array 9 :displaced-to a))) 
 #2A(("<0,0>" "<0,1>" "<0,2>") 
     ("<1,0>" "<1,1>" "<1,2>") 
     ("<2,0>" "<2,1>" "<2,2>")) 
 #("<0,0>" "<0,1>" "<0,2>" "<1,0>" "<1,1>" "<1,2>" "<2,0>" "<2,1>" "<2,2>") 
 #<ARRAY 9 indirect 36363476>

22.1.3.10 Printing Random States

A specific syntax for printing objects of type random-state is not specified. However, every implementation must arrange to print a random state object in such a way that, within the same implementation, read can construct from the printed representation a copy of the random state object as if the copy had been made by make-random-state.

If the type random state is effectively implemented by using the machinery for defstruct, the usual structure syntax can then be used for printing random state objects; one might look something like

#S(RANDOM-STATE :DATA #(14 49 98436589 786345 8734658324 ... ))

where the components are implementation-dependent.

22.1.3.11 Printing Pathnames

When printer escaping is enabled, the syntax #P"..." is how a pathname is printed by write and the other functions herein described. The "..." is the namestring representation of the pathname.

When printer escaping is disabled, write writes a pathname P by writing (namestring P) instead.

For information on how the Lisp reader parses pathnames, see Section 2.4.8.14 (Sharpsign P).

22.1.3.12 Printing Structures

By default, a structure of type S is printed using #S syntax. This behavior can be customized by specifying a :print-function or :print-object option to the defstruct form that defines S, or by writing a print-object method that is specialized for objects of type S.

Different structures might print out in different ways; the default notation for structures is:

#S(structure-name {slot-key slot-value}*)

where #S indicates structure syntax, structure-name is a structure name, each slot-key is an initialization argument name for a slot in the structure, and each corresponding slot-value is a representation of the object in that slot.

For information on how the Lisp reader parses structures, see Section 2.4.8.13 (Sharpsign S).

22.1.3.13 Printing Other Objects

Other objects are printed in an implementation-dependent manner. It is not required that an implementation print those objects readably.

For example, hash tables, readtables, packages, streams, and functions might not print readably.

A common notation to use in this circumstance is #<...>. Since #< is not readable by the Lisp reader, the precise format of the text which follows is not important, but a common format to use is that provided by the print-unreadable-object macro.

For information on how the Lisp reader treats this notation, see Section 2.4.8.20 (Sharpsign Less-Than-Sign). For information on how to notate objects that cannot be printed readably, see Section 2.4.8.6 (Sharpsign Dot).

22.1.4 Examples of Printer Behavior

 (let ((*print-escape* t)) (fresh-line) (write #\a)) 
 #\a 
 #\a 
 (let ((*print-escape* nil) (*print-readably* nil)) 
   (fresh-line) 
   (write #\a)) 
 a 
 #\a 
 (progn (fresh-line) (prin1 #\a)) 
 #\a 
 #\a 
 (progn (fresh-line) (print #\a)) 
 
 #\a 
 #\a 
 (progn (fresh-line) (princ #\a)) 
 a 
 #\a 


 (dolist (val '(t nil)) 
   (let ((*print-escape* val) (*print-readably* val)) 
     (print '#\a) 
     (prin1 #\a) (write-char #\Space) 
     (princ #\a) (write-char #\Space) 
     (write #\a))) 
 #\a #\a a #\a 
 #\a #\a a a 
 NIL 


 (progn (fresh-line) (write '(let ((a 1) (b 2)) (+ a b)))) 
 (LET ((A 1) (B 2)) (+ A B)) 
 (LET ((A 1) (B 2)) (+ A B)) 


 (progn (fresh-line) (pprint '(let ((a 1) (b 2)) (+ a b)))) 
 (LET ((A 1) 
       (B 2)) 
   (+ A B)) 
 (LET ((A 1) (B 2)) (+ A B)) 


 (progn (fresh-line) 
        (write '(let ((a 1) (b 2)) (+ a b)) :pretty t)) 
 (LET ((A 1) 
       (B 2)) 
   (+ A B)) 
 (LET ((A 1) (B 2)) (+ A B)) 


 (with-output-to-string (s) 
    (write 'write :stream s) 
    (prin1 'prin1 s)) 
 "WRITEPRIN1"