Re: new function or modify read
Marc Feeley 17 Dec 2002 21:58 UTC
> Look at this, please, from the perspective of having a well-behaved
> function that can write an external representation of data so that it
> can be read back in recovering the same structure, and you will see
> that it's a completely different issue from presenting the data in a
> particular style, format, or radix. Such details are irrelevant here.
This is not at all my perspective. I manipulate large cyclic data
structures all the time (doubly-linked lists, trees with
back-pointers, object system class descriptors, etc), my programs
(sometimes!) have bugs and I want to dump these cyclic data-structures
nicely so that I can figure out what the problem is. I do this all
the time! It would be a shame if I could not combine the
pretty-printing feature with the shared data printing feature. My
point is that these features are not mutually exclusive, so the API
should be designed to let the user pick the features needed.
Moreover, if you are truly trying to design a procedure that will
allow data to be read back and recover the same structure, then there
are still plenty of on/off switches that you can give to the user (and
that you eventually will have to as the needs of users evolve):
- Should hidden fields of an object be written or not? This is
useful for debugging but also for saving an object to a file and
reading it back in later.
- Should numbers that are eq? be marked as shared or not? For
example in (123 . 123) both numbers are probably eq?, and perhaps
also in (0.0 . 0.0).
- Should the low-level code of procedures (bytecode, or whatever) be
dumped? Same issue for continuations.
- Should the object be written as text or in binary? After all if a
human is not going to read the data using text is just wasteful.
A binary representation, or even a special purpose textual
representation would be more compact.
So in the end the "write-showing-shared" procedure still needs
parameters. We don't yet know how many we need (and probably never
will as the need for new features never ceases to grow). This is why
an API based on dynamically bound parameter objects is needed.
Alternatively, explicit keyword parameters could also be used:
(write obj port shared: #t pretty: #t)
I have also found readtables to be a good way to package up the
parameters that are used by "write" and "read". The readtable
specifies the external representation and "write" and "read" use
the readtable when generating the external representation or
when parsing an object. With readtables you would have:
(let ((rt (readtable-copy (current-readtable))))
(readtable-allow-sharing-set! rt #t)
(readtable-pretty-print-set! rt #t)
(parameterize ((current-readtable rt))
(write obj port)))
or
(let ((rt (readtable-copy (current-readtable))))
(readtable-allow-sharing-set! rt #t)
(readtable-pretty-print-set! rt #t)
(write obj port readtable: rt))
Marc