[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
I/O proposal
>From Gary Brooks and William Clinger at Indiana U:
This is an interim proposal. It takes an old-fashioned view of
streams, and it does nothing about windows and graphics. Arguments to
functions are delimited by angle brackets (<...>) and optional arguments
are further delmited by curly brackets ({...}).
(eof? <object>) Essential
EOF? takes an object and returns true if the object is an end of
file object. The precise set of end of file objects will vary
among implementations, but in any case no end of file object will
ever be a character or an object that can be read in using READ.
Rationale:
A fixed set of end of file objects seems to be simpler than
allowing each input procedure to take yet another optional
argument specifying what to do on end of file. Allowing for a
set of such objects rather than a single such object allows for
different implementation strategies.
(read {<stream>}) Essential
READ takes an input stream as argument and returns the next
object parsable from the stream, updating the stream to point to
the first character past the end of the written representation of
the object. If an end of file is encountered in the input before
any characters are found that can begin an object, then an end of
file object is returned. If an end of file is encountered after
the beginning of an object's written representation, but the
written representation is incomplete and therefore not parsable,
an error must be signalled. The stream argument to READ may be
omitted, in which case it defaults to (CURRENT-INPUT-STREAM).
Rationale:
READ corresponds to Common Lisp's READ-PRESERVING-WHITESPACE.
This will require something like UNREAD-CHAR or PEEK-CHAR
internally. For simplicity, it is never an error to encounter
end of file when READ is called.
(write <object> {<stream>}) Essential
WRITE takes an object and an output stream and writes a
representation of the object to the stream. Strings that appear
in the written representation are enclosed in double quotes, and
within those strings backslash and double quote characters are
escaped by backslashes. (Implementations that allow
slashification within symbols will probably want WRITE to
slashify funny characters in symbols as well.) WRITE returns an
unspecified value. The stream argument to WRITE may be omitted,
in which case it defaults to (CURRENT-OUTPUT-STREAM).
(display <object> {<stream>}) Essential
DISPLAY takes an object and an output stream and writes a
representation of the object to the stream. Strings that appear
in the written representation are not enclosed in double quotes,
and no characters are escaped within those strings. DISPLAY
returns an unspecified value. The stream argument to DISPLAY may
be omitted, in which case it defaults to (CURRENT-OUTPUT-STREAM).
Rationale:
WRITE is for producing machine-readable output, DISPLAY for
producing human-readable output.
(newline {<stream>}) Essential
NEWLINE takes an output stream and writes a newline to the
stream. NEWLINE returns an unspecified value. The stream
argument to NEWLINE may be omitted, in which case it defaults to
(CURRENT-OUTPUT-STREAM).
Rationale:
NEWLINE abstracts away from the implementation details of
ending a line (or is it starting a new one?).
(listen? {<stream>}) Optional
LISTEN? takes an input stream (an interactive stream in the
useful case) and returns true if a character is ready on that
stream so that a READ-CHAR operation will not hang and returns
false if a READ-CHAR operation will hang. If the stream is at
end of file then the value returned by LISTEN? is unspecified.
Rationale:
LISTEN? is needed for interactive input streams.
(read-char {<stream>}) Essential
READ-CHAR takes an input stream and returns the next character
available from the stream, updating the stream to point to the
following character. If no more characters are available from
the stream, an end of file value is returned. The stream
argument to READ-CHAR may be omitted, in which case it defaults
to (CURRENT-INPUT-STREAM).
(display-char <character> {<stream>}) Essential
DISPLAY-CHAR takes a character and an output stream, and writes
the character itself (not a written representation of the
character) to the stream. DISPLAY-CHAR returns an unspecified
value. The stream argument to DISPLAY-CHAR may be omitted, in
which case it defaults to (CURRENT-OUTPUT-STREAM).
Rationale:
READ-CHAR and DISPLAY-CHAR are for low-level I/O.
(load <filename>) Essential
LOAD takes a string that names an existing file containing Scheme
source code. It reads Scheme expressions from the file and
interprets them sequentially as though they had been typed
interactively. It is not specified whether the results of the
expressions are printed. Neither is it specified whether or not the
LOAD procedure affects the values returned by (CURRENT-INPUT-STREAM)
and (CURRENT-OUTPUT-STREAM) during the loading process. LOAD returns
an unspecified value.
Rationale:
For portability LOAD must operate on source files. Its operation
on other kinds of files necessarily varies among implementations.
(transcript-on <file-name>) Optional
TRANSCRIPT-ON takes a string that names an output file to be
created, and returns an unspecified value. The effect of
TRANSCRIPT-ON is to open the named file for output, and to cause
a transcript of subsequent interaction between the user and the
Scheme system to be written to the file. The transcript is ended
by a call to TRANSCRIPT-OFF. Only one transcript may be in
progress at any time. (Some implementations may relax this
restriction.)
(transcript-off) Optional
TRANSCRIPT-OFF takes no arguments and returns an unspecified
value. It ends any transcript in progress and closes the
transcript file.
Rationale:
TRANSCRIPT-ON and TRANSCRIPT-OFF are redundant in some systems,
but systems that need them should provide them.
(stream? <object>) Essential
STREAM? takes one argument and returns true iff its argument is a
stream.
Rationale:
Perhaps STREAM? should be subdivided into INPUT-STREAM? and
OUTPUT-STREAM?.
(current-input-stream) Essential
CURRENT-INPUT-STREAM takes no arguments and returns the current
default input stream.
(current-output-stream) Essential
CURRENT-OUTPUT-STREAM takes no arguments and returns the current
default output stream.
Rationale:
CURRENT-INPUT-STREAM and CURRENT-OUTPUT-STREAM are procedures
rather than variables to allow for various ways to implement the
default streams. Explicit fetching of the default streams is
convenient for programs that want to change the default stream
using WITH-INPUT-FROM-FILE or WITH-OUTPUT-TO-FILE and still use
the original default stream for some I/O; see below.
(call-with-input-file <string> <procedure>) Essential
CALL-WITH-INPUT-FILE takes a procedure of one argument and a
string naming an existing file and calls the procedure with the
stream obtained by opening the named file for input. If the
file cannot be opened, an error should be signalled. If the
procedure returns, then the stream is closed automatically and
the value yielded by the procedure is returned by
CALL-WITH-INPUT-FILE. If the current continuation ever changes in
such a way as to make it doubtful that the procedure will return,
CALL-WITH-INPUT-FILE may close the input stream. The exact
interaction of escape procedures with CALL-WITH-INPUT-FILE is
unspecified.
(call-with-output-file <file-name> <procedure>) Essential
CALL-WITH-OUTPUT-FILE takes a procedure of one argument and a string
naming a file to be created and calls the procedure with the stream
obtained by opening the named file for output. If the file cannot be
be opened, an error should be signalled. If a file with that name
already exists, the effect is unspecified. If the procedure returns,
then the stream is closed automatically and the value yielded by the
procedure is returned by CALL-WITH-OUTPUT-FILE. If the current
continuation ever changes in such a way as to make it doubtful that
the procedure will return, CALL-WITH-OUTPUT-FILE may close the output
stream. The exact interaction of escape procedures with
CALL-WITH-OUTPUT-FILE is unspecified.
Rationale:
CALL-WITH-INPUT-FILE and CALL-WITH-OUTPUT-FILE are convenient for
programs that need to read input from or send output to several
directions at once. They cannot be synthesized from
WITH-INPUT-FROM-FILE and WITH-OUTPUT-TO-FILE so they are the essential
procedures. Whether or not they close files when there is a throw out
of the procedure is mainly a performance issue, of greatest importance
when there is a small limit on the number of files that can be open at
once. If they close files for a throw, then the vagueness of what it
means for the current continuation to change in such a way as to make
it doubtful that a procedure will return, the possibility of a throw
back in, and the vagueness of what it means to close a file, make it
unwise to specify the exact interaction of the file-closing mechanism
with escape procedures.
It is arguable whether the notion of closing a file should be
reflected in the semantics of Scheme. Operating systems require
that files be closed for the following reasons:
1. To reclaim storage. This should be left to the garbage
collector.
2. To prevent some table in the operating system from
overflowing because there are too many files open at once.
Only implementations that have this problem should have to
worry about it.
3. To facilitate file locking. This seems to be the only
semantic reason for closing a file, and even so there are
implementations for which it isn't meaningful.
Because some systems have to worry about closing files, however,
all portable Scheme code must worry about it. We have tried to
follow MIT's lead in isolating the worry within a few standard
procedures.
(open-input-file <file-name>) Optional
OPEN-INPUT-FILE takes a string naming an existing file and
returns an input stream capable of delivering characters from the
file. If the file cannot be opened, an error should be signalled.
(open-output-file <file-name>) Optional
OPEN-OUTPUT-FILE takes a string naming an output file to be
created and returns an output stream capable of writing
characters to a new file by that name. If the file cannot be
opened, an error should be signalled. If a file with the given
name already exists, the effect is unspecified.
(close-input-stream <stream>) Optional
CLOSE-INPUT-STREAM takes an input stream and returns an
unspecified value. If the stream is connected to a file, the
file is closed and the stream rendered incapable of delivering
characters.
(close-output-stream <stream>) Optional
CLOSE-OUTPUT-STREAM takes an output stream and returns an
unspecified value. If the stream is connected to a file, the
file is closed and the stream rendered incapable of writing
characters to it.
Rationale:
OPEN-INPUT-FILE, OPEN-OUTPUT-FILE, CLOSE-INPUT-STREAM, and
CLOSE-OUTPUT-STREAM are needed to use streams whose lifetimes are
not properly nested, which is to say they are seldom needed.
OPEN-INPUT-FILE and OPEN-OUTPUT-FILE are distinct to avoid the
need for a switch argument; if independent switches were
required, it would be better to use switches rather than have a
product space of procedures. CLOSE-INPUT-STREAM and
CLOSE-OUTPUT-STREAM could probably be combined into CLOSE-STREAM
with no great loss. A different set of procedures will
be needed to create streams from and to strings, and the closing
procedures should still work on them. It seems that closing a
string output stream should yield a string, but it isn't clear
what should be returned when a file output stream is closed.
(with-input-from-file <file-name> <thunk>) Optional
WITH-INPUT-FROM-FILE takes a string and a thunk (a procedure of
no arguments). The string must name an existing file. The file
is opened for input, an input stream connected to it is made the
default value returned by (CURRENT-INPUT-STREAM), and the thunk
is invoked. When the thunk returns, (CURRENT-INPUT-STREAM) is
closed and the previous default value returned by
(CURRENT-INPUT-STREAM) is restored. WITH-INPUT-FROM-FILE returns
the value returned by the thunk. Furthermore WITH-INPUT-FROM-FILE
should attempt to close the input stream and restore the default
input stream whenever the current continuation changes in such a
way as to make it doubtful that the thunk will ever return.
(with-output-to-file <file-name> <thunk>) Optional
WITH-OUTPUT-TO-FILE takes a stfung and a thunk. The string names
an output file to be created. The file is opened for output, an
output stream connected to it is made the default value returned
by (CURRENT-OUTPUT-STREAM), and the thunk is invoked. When the
thunk returns, (CURRENT-OUTPUT-STREAM) is closed and the previous
default value returned by (CURRENT-OUTPUT-STREAM) is restored.
WITH-OUTPUT-FROM-FILE returns the value returned by the thunk.
Furthermore WITH-OUTPUT-FROM-FILE should attempt to close the
output stream and restore the default output stream whenever the
current continuation changes in such a way as to make it doubtful
that the thunk will ever return.
It has been suggested that if evaluation of the thunk is
abandoned and later continued then the file should be re-opened
in exactly the same state that it had been in when it was last
closed, but this may be awkward or inefficient in some
implementations.
Rationale:
WITH-INPUT-FROM-FILE and WITH-OUTPUT-TO-FILE are sufficient for
programs that only use one input or one output at a time. Indeed
by nesting them and by fetching the various nested streams using
CURRENT-INPUT-STREAM and CURRENT-OUTPUT-STREAM, arbitrarily many
input or output files may be used. The automatic closing of the
streams may not be terribly important in some implementations,
but the fact that the old defaults are restored is important
semantically. There are several incompatible ways that they
could interact with escape procedures, however, and at this time
it isn't entirely clear which ways are best. See rationale for
CALL-WITH-OUTPUT-FILE and CALL-WITH-INPUT-FILE.