What is print-read consistency?

And how important is it, when defining a print-object method?

For example, it appears to me that hash-table instances in SBCL violate this principle.


Print-read consistency says, in CL, that if *print-readably* is true, then printing an object should either:

  • produce a printed representation which is similar to the object when read with the standard readtable in effect;
  • or signal an error of type print-not-readable.

Similarity has a slightly complicated definition, because it is necessarily not definable within a single image, since the whole purpose of it is to allow objects to be printed and then later read in another image. It is defined in 3.2.4.2 of the spec, although all of 3.2.4 is worth reading. Note in particular that implementations are allowed to extend the definition of similarity.

Note that there are some lacunae in my definition above: in particular I think that the other printer & reader control variables need to have standard values for *print-readably* to make sense (for instance *package*, *read-default-float-format* and so on. with-standard-io-syntax is the tool you want here.

Finally note that similarity is defined for multiple images of the same implementation in some cases. For objects whose readable printed representation is not defined by the standard, an implementation is allowed to print things such that another instance of itself can reconstruct a similar object: it doesn't have to ensure that other implementations can.

What all this means is that if you are defining a mechanism for printing an object then, if *print-readably* is true, it should either print the object in such a way that a similar object will be read (in the same implementation perhaps), or signal an appropriate error: what it should not do is quietly print the object in an unreadable way or in a way such that reading the representation does not return a similar object. A good way of doing this is to use print-unreadable-object whose whole purpose is to ensure this for you.

Hash tables may or may not be printable readably. In SBCL they are:

* (defvar *h* (make-hash-table))
*h*
* (setf (gethash 'foo *h*) 1)
1
* *h*
#<hash-table :TEST eql :COUNT 1 {1002CE5863}>
* (with-standard-io-syntax (print *h*))

#.(SB-IMPL::%STUFF-HASH-TABLE (MAKE-HASH-TABLE) (QUOTE ((FOO . 1)))) 
#<hash-table :TEST eql :COUNT 1 {1002CE5863}>

The #.(...) form will recreate a similar hashtable when read, in SBCL (but only in SBCL).

In another implementation (Clozure CL), hash tables are not printable readably:

? (defvar *h* (make-hash-table))
*h*
? (setf (gethash 'foo *h*) 1)
1
? *h*
#<hash-table :test eql size 1/60 #x3020012A119D>
? (with-standard-io-syntax (print *h*))

> Error: Attempt to print object #<HASH-TABLE :TEST EQL size 1/60 #x3020012A119D> on stream #<SYNONYM-STREAM to *TERMINAL-IO* #x302000B9DC1D>