[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

Memorial Day Ballot: comments on other people's entries

	14. No.  I don't think the user needs to be concerned with whether a
	    call to READ is at top level or not, since READ has to use a
	    special variable to keep track of its state.

    The issue is how do you know when to re-bind this special variable?  What
    if a reader macro, while reading from a file, asks for some input from the
    terminal for some reason?  What if the same reader macro does the same thing
    while reading from the terminal?  What if the reason it asked for input wasn't
    that it was programmed to, but that it got an error?  READ can't tell whether
    it was called with the intent of being a recursive call or the intent of being
    a top-level call, although there are ways it can guess that work a lot of
    the time.

I guess I have to go along with this.  The solution proposed in the
ballot still isn't good enough though, because reader macros have to
know whether they're being called at the top level or not so that they
in turn will know whether to call READ or SUB-READ.  I would like to
propose the following instead:

Reader macros take three arguments: the input stream, the macro
character, and the reader state.  The third argument is an object which
holds the state of the reading process at the point where the macro
character was encountered.  READ and READ-PRESERVING-WHITESPACE take an
optional fourth (and READ-DELIMITED-LIST an optional third) READER-STATE
argument.  If a reader macro calls any of these three functions it
should supply its third argument as the callee's READER-STATE argument.
In the case of READ and READ-PRESERVING-WHITESPACE, a NIL value for
EOF-ERRORP in this call may be overridden by a non-NIL value supplied by
(or defaulted in) a dynamically enclosing call and encoded in the reader
state object.

This doesn't require any new functions, and it means that the
reader doesn't have to keep any global state.  On the other hand, it
does add another data type, READER-STATE, which is similar to
RANDOM-STATE, i.e. at the user level it's just a token that gets passed
around.  It also means that READ takes four optional arguments, but
the fourth is only used in very unusual circumstances.

Allowing user hooks into processes like reading, printing and evaluation
(and even macro expansion, as in COMPILER-LET) tends to lead to problems
like these.  For example, there are a bunch of special variables which
act as flags to control the behavior of the printer.  There is also a
function, WRITE, which takes keyword arguments for these flags.
However, a function given as an argument to the :PRINT-FUNCTION option
of DEFSTRUCT is supposed to observe the values of the variables in its
behavior.  Therefore, WRITE must bind all those special variables to the
values supplied for the keywords (which by default of course are the
values of the special variables).  I can't wait to see the lambda list
for that one!  Luckily that only infringes on the implementor, not the
user, but it is an unexpected consequence of allowing that hook.  And as
I look at it, I see that in order for *PRINPRETTY* (make that
*PRINT-PRETTY*) to be useful, the :PRINT-FUNCTION should be supplied
with a "horizontal position" argument, which should also be passed to
recursive calls to the printer.  What units, you say?  I have discovered
the general solution to this problem, but unfortunately there isn't
enough space in this editor buffer to fully descr