Status: Passed, Jan 89 X3J13, as amended
References: pp. 370-371
Edit history: Version 4, Masinter, 17-Mar-89 (as amended)
Version 3, Masinter 10/8/88 (minor cleanup)
Version 2, Chris McConnell 10/05/88
Version 1, Chris McConnell 09/20/88
When a lisp object is printed that points to a structure with a user
defined print-function and there is a pointer back to the containing
object, the printer will recurse infinitely even when *print-circle*
is set to T. This prevents printing circular structures that point to
objects that cannot be printed and prevents the development of new
printed representations that can be read by the reader.
When *print-circle* is T, a user defined print-function can print
objects to the supplied stream using WRITE, PRIN1, PRINC, or FORMAT
and expect circularities to be detected and printed using #n# syntax.
If a user defined print-function prints to a stream other than the one
that was supplied, then circularity detection starts over for that
stream. This applies to methods on PRINT-OBJECT in addition to
;;; Define a structure that can be circular and that has a slot with a
;;; value that cannot be printed.
(defstruct (TEST (:print-function print-test))
(function #'(lambda (x)
(error "No function is defined."))))
;;; This print function generates a machine readable printed
;;; representation for a structure with a slot that cannot be printed.
(defun PRINT-TEST (structure stream depth)
(format stream "#S(TEST PTR ~S)" (test-ptr structure)))
;;; Define two structures that point to each other. If this
;;; expression successfully evaluates at the top level, then the
;;; printed result should look like:
;;; #1=#S(TEST PTR #S(TEST PTR #1#))
;;; If it does not work then it will generate an infinite printed
(setf *print-circle* t
(test-ptr *a) *b
(test-ptr *b) *a)
Many structures are circular and point to complex data structures that
may include things like closures that cannot be printed. It should be
possible to define a way to print these data structures such that they
can be read back in. Common LISP provides two mechanisms for these
problems (*print-circle* and the :print-function option to defstruct),
but they do not currently work together in most implementations.
Lucid 3.0 and the Genera do work on the test case. (Previous versions
did not.) KCL, Coral and Franz do not work.
Cost to Implementors:
Lucid and Symbolics have done it, so they could give an idea of the
difficulty. Possible techniques include passing the printer state
information by dynamic binding rather than by explicit parameters or
by supplying an internal stream to the user print function.
Cost to Users: None
Cost of non-adoption:
Currently this problem causes an infinite recursion whenever a user
print-function prints a lisp object that contains the structure that
is being printed by the user print function. If nothing is done, this
error will remain and the whole effort to provide a portable printed
representation of LISP structures is of minimal usefulness. In almost
any real application, there are circular structures with non printable
objects such as closures and hash tables that need to be printed. In
addition the development of printers for new reader macros becomes
much more of an effort then it should be since it requires
reimplementing the complete circularity detection mechanism.
If the proposal is adopted, then it becomes easier to define new
printed representations that are compact and that still capture the
information needed to rebuild data structure instances. It allows a
printed representation to hide the actual details of how a data
structure is implemented in terms of underlying LISP data structures.
This proposal increases the uniformity of the language by making
*print-circle* work in all cases including where a user has defined a
new print function.
At least three members of the cleanup committee read this and think
it looks good.