[no subject]

To: "MOON at SCRC-TENEX" at MIT-AI

From: Scott E. Fahlman <Fahlman at Cmu-20c>

Date: Tue, 10 Aug 1982 03:28:00 -0000

Cc: Common-Lisp at SU-AI, Slisp: at CMU-20C

Dave,
The SLISP: address is a personal mailing list of Spice Lisp people at
CMU. I am in the process of making this public so that mail to SLISP @
CMUC will work. In the meantime, I will forward things.
Fateman just sent me a chunk of code from the depths of Macsyma
(believed to be a relic of the legendary RWG) from which I think we can
extract the necessary algorithm for RATIONALIZE, so strike that whole
comment.
You are right -- the paragraph I sent on function streams is
incomprehensible even to me. Sorry, it's been a rough month. The
attempted clarification follows:
It would be convenient for many purposes to have a type of output stream
that accepts characters or bytes but, instead of sending them off to a
file, passes the data to a user-supplied function, perhaps a closure.
Similarly, it would be useful to have a type of input stream that, when
asked for some input, calls a user-supplied function to obtain the data,
rather than sucking the characters or bytes in from a file. This
mechanism could be used to implement such things as broadcast and string
streams, if these were not built in already. Presumably there will be a
need for more such hacks in the future, and this mechanism gives us a nice
flexible hook.
What I propose is the following:
MAKE-FUNCTION-INPUT-STREAM fn [function]
MAKE-FUNCTION-OUTPUT-STREAM fn [function]
MAKE-FUNCTION-IO-STREAM fn [function]
These functions create and return special stream objects that can be
used wherever regular input, output, and i/o stream objects are legal.
FN must be a function that accepts one required argument and a &rest
argument. When some I/O operation is called on one of these streams,
the name of that operation (a symbol such as OUCH) is passed to FN as
the first argument, and all of that operation's arguments (evaluated)
are passed to FN as additional arguments. Whatever FN returns is
returned by the OUCH (or whatever) as its value.
For example, if X is a function output stream whose associated function
is FX, and we do (OUCH #\a X), we end up calling FX with arguments OUCH,
#\a, and the value of X. The FX function can then do whatever it wants
to with the #\a -- perhaps encrypt it and shove it into a string, or
play an "a" tone on the noisemaker, or whatever. Clearly, the FX will do
a big dispatch on its first argument and then will process the other args
accordingly. Whatever FX returns is the return value of the OUCH, tail
recursively.
The user might or might not want FX to handle all of the more esoteric
operations, such as FORCE-OUTPUT. If FX recognizes FORCE-OUTPUT as its
first argument and does something useful, fine; if not, the big dispatch
will fall through and, by convention, an
:UNKNOWN-OPERATION-TO-FUNCTION-STREAM error will be signalled.
(We might want to give that a shorter name.)
Everyone who has seen this proposal has noticed that it is extremely
flavor-like. I don't think we want to let flavors permeate the language
-- not yet, anyway -- but I don't object to the sort of message-passing
protocol used here. It is the inheritance and flavor-mixing parts of
the flavor system that I don't trust, not the basic idea of active
objects and message-passing interfaces.
-- Scott