Article View: mail.common-lisp
Article #1Printing circular structures
From: Chris McConnell
Date: Fri, 24 Jun 1988 04:29
Date: Fri, 24 Jun 1988 04:29
92 lines
4081 bytes
4081 bytes
There is an ambiguity in how CL handles the printing of circular
references when a print-function is defined for a structure. I want
to write out possibly circular structures in such a way that they can
be read back in. As long as I want to print all of the slots in the
structures, I can do this by setting *print-circle* to T. But, I have
defstructs that point to objects that can't be printed like closures.
What I think I should be able to do is define a :print-function for my
structure that prints out #S syntax without all of the slots.
(defstruct (woof (:print-function print-woof))
a b)
(defun print-woof (woof stream depth)
(format stream "#S(WOOF A ~A)" (woof-a woof)))
(progn
(setf *a (make-woof)
*b (make-woof)
(woof-a *a) *b
(woof-b *a) (let* ((a 3))
#'(lambda (x) (+ x a)))
(woof-a *b) *a
*print-circle* t)
(print *a))
Some implementations handle this, but most don't. My reading of the
spec says that the printer will handle circular references when
*print-circle* is T. It doesn't say anything about not handling it
when you have print functions on structures. One way to get this
behavior is to have write check for circular structures something like
this:
;;;
(defvar *REAL-PRINT-CIRCLE* nil
"Global to control printing of circular objects.")
(defvar *PRINT-HASH-TABLE* (make-hash-table)
"Hash table from objects being printed to their reader number.")
(defvar *PRINT-COUNTER* 0
"Current counter number.")
;;;
(unless (fboundp 'old-write)
(setf (symbol-function 'old-write) (symbol-function 'write)))
;;;
(defun WRITE (object
&rest args
&key
(circle *print-circle*)
(stream *standard-output*)
&allow-other-keys)
"Fixed write function."
(if (or (symbolp object) (numberp object) (stringp object))
;; Never have circular references
(apply #'old-write object args)
(if *real-print-circle*
;; We've already entered write the circle T
(let* ((number (gethash object *print-hash-table*)))
(if number
(format stream "#~A#" number)
(progn
(setf (gethash object *print-hash-table*)
(incf *print-counter*))
(format stream "#~A=" *print-counter*)
(apply #'old-write object args))))
(progn
(if circle
;; First time with circle T
(let* ((*real-print-circle* t))
(setf (getf args :circle) nil)
(apply #'write object args)
(setq *print-counter* 0)
(clrhash *print-hash-table*))
;; Circle is nil
(apply #'old-write object args)))))
object)
This approach does generate a #n for every list, array, etc. If I set
*print-circle* to T, I am perfectly willing to have a #n= for every
list, array and structure. No it is not pretty, but the primary
purpose of the #n syntax is not to make it so that its pretty, but to
make it so that objects can be written and then read back in using the
reader. Since CLTL explicitly specifies that some things (like
closures) do need to be written in a way that can be read, I do not
think that it is unreasonable to have the printer support
print-functions that suppress those values. I would write this stuff
myself, except that there is no portable way to find the names of
slots in defstructs. (Even if there were, monsterous defstructs like
those used in CLOS where every slot is an array with all sorts of
garbage in them would still be a problem.)
Has this been addressed by the cleanup committee?
Technical Details
Message-ID:
<4555@zodiac.UUCP>Path:
arc1-ca.pugleaf.net!utzoo!attcan!uunet!lll-winken!lll-tis!ames!zodiac!mcconnelShow Full Headers
Complete Article Headers
Path: arc1-ca.pugleaf.net!utzoo!attcan!uunet!lll-winken!lll-tis!ames!zodiac!mcconnel From: mcconnel@zodiac.ads.com (Chris McConnell) Newsgroups: mail.common-lisp,comp.lang.lisp Subject: Printing circular structures Message-ID: <4555@zodiac.UUCP> Date: 24 Jun 88 04:29:47 GMT Sender: news@zodiac.UUCP Reply-To: mcconnel@ads.com (Chris McConnell) Organization: Advanced Decision Systems, Mt. View, CA (415) 960-7300 Lines: 91