Email list hosting service & mailing list manager

Re: new function or modify read Marc Feeley 18 Dec 2002 15:47 UTC

> >  - 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.
> This is immaterial since "hidden fields" are a completely undefined

The list of on/off switches I mentioned in my message was just an
example to show that many variations exist.  I was not suggesting that
you add them to the SRFI, in fact for most of the switches it is
clearly premature to finalize a design.

My point is that there are many variations to "writing data so that it
can be read back".  The fact that many of them are implementation
dependent is not important.  It just means that individual
implementors will be faced with the problem of expressing these
variations.  Because there are so many variations (at least at some
point in the future) and they are not mutually exclusive, the
variations should not be expressed through the name of the procedure.
Parameters should be used.  Your choices are:

  1) positional parameters
  2) keyword parameters
  3) dynamically scoped parameter objects

I have expressed my preference between these three.  But this
preference is a detail.  The important thing is to have a
parameterized procedure.

Let me move on to a second topic.  Orthogonal to the particular
parameter passing being used is the issue of whether the options
should be packaged or not into one structure.  This can be a
procedure, as you suggested, or as a data structure, that is the
"readtable" that I suggested.  Now don't be fooled by the name
"readtable" which was chosen for historical reasons (and because it is
a short name).  It is really a data structure that specifies the
external representation of data.  This information is both useful for
"write" and for "read".  In fact, to guarantee that "read" is able to
read data that has been written with "write", the same readtable must
be supplied to "read" and "write".  For example, here is some of the
information that can be found in a Gambit-C readtable:

  - Whether sharing is allowed (i.e. the notation #0#, #0=...).

  - The maximum depth and length that should be written.

  - Whether case should be preserved in symbols, character names, etc.
    The choices are: yes, no and map to lowercase, no and map to uppercase.

  - Whether keywords are allowed.
    The choices are: no, yes and ":" is at end, yes and ":" is at start.

  - Whether control characters in strings should be escaped.

  - The set of characters that can be escaped in a string (i.e. \n, \\, etc).

  - The set of characters that are named (i.e. #\space, #\newline, etc).

  - The set of special #! objects (i.e. #!eof, #!void, etc).

  - Whether evaluation is allowed (i.e. the notation #.<expression>).

  - The formatting rules for pretty-printing.

Once again this is just an example.  I'm not suggesting that you
handle all of these.  You could limit yourself to the first one
and leave the "readtable" type open for extensions.

Readtables and dynamically scoped parameter objects can be combined in
an interesting way.  Imagine that to each port is attached a parameter
object that corresponds to the readtable that is used for calls to
"read" and "write" on that port.  When a port is created some sort of
"standard" readtable is used (which makes "write" and "read" behave as
specified in R5RS).  Suppose that (port-readtable-parameter port)
returns the parameter object that is attached to the port and that
(readtable-shared-set readtable flag) returns a copy of the readtable
where the "shared" option is set to flag (it does *not* mutate the
readtable passed as an argument).  This gives a very flexible API:

- You can "write" in a standard way:

    (write X port)

- You can setup a port so that all output is in a particular style:

    (let ((port (open-output-port "foo")))

      (let ((rt-param (port-readtable-parameter port)))
        (rt-param (readtable-shared-set (rt-param) #t)))

      (write X port) ; uses the sharing notation
      (write Y port) ; uses the sharing notation

- You can temporarily do output in a particular style, on a specific port:

    (let ((port (open-output-port "foo")))

      (let ((rt-param (port-readtable-parameter port)))
        (parameterize ((rt-param (readtable-shared-set (rt-param) #t)))
          (write X port))) ; uses the sharing notation

      (write Y port) ; uses the standard notation

- You can have different threads doing output in their own particular style
  (this could be useful if you have a REPL thread and a "main program" thread
  and you want output from the REPL to be in a particular style):

    (let ((port (open-output-port "foo")))

          (lambda ()
            (let ((rt-param (port-readtable-parameter port)))
              (parameterize ((rt-param (readtable-shared-set (rt-param) #t)))
                ...body of the child thread...))))) ; uses the sharing notation

      ... ; the parent thread uses the standard notation

Again I'm not suggesting you go this far.  The main point of my
message(s) is to make your procedure parameterizable so that such
extensions are possible (to be defined in other SRFIs).