Up Next

11.1  Streams

Input and output in ECLiPSe is done via communication channels called streams. They are usually associated with I/O devices (a file, a terminal, a socket, a pipe), or in-memory queues or buffers.

The streams may be opened for input only (read mode), output only (write mode), or for both input and output (update mode).

11.1.1  Predefined Streams

Every ECLiPSe session defines the following symbolic stream names, which indicate the current streams for certain categories of input/output:

input
Used by the input predicates that do not have an explicit stream argument, e.g., read/1. This is by default the same as user_input and stdin, but can be redirected.
output
Used by the output predicates that do not have an explicit stream argument, e.g., write/1. This is by default the same as user_output and stdout, but can be redirected.
error
Output for error messages and all messages about exceptional states. This is by default the same as user_error and stderr, but can be redirected.
warning_output
Used by the system or user programs to output warning messages. This is by default the same as user_output and stdout, but can be redirected.
log_output
Used by the system for information messages (e.g. garbage collection), or by user programs for e.g. messages about computational progress. This is by default the same as user_output and stdout, but can be redirected.

The above streams can be freely redirected, but are initially set to one of the following three default streams, to which they will also be reset whenever a redirection ends:

user_input
The default input stream. This is initially the same as stdin, but can be redirected.
user_output
The default output stream. This is initially the same as stdout, but can be redirected.
user_error
The default error stream. This is initially the same as stderr, but can be redirected.

Finally, there are the four predefined standard streams, which cannot be closed or redirected. Apart from the null stream, there is usually no need to refer to them explicitly:

stdin
The standard input stream.
stdout
The standard output stream.
stderr
The standard error stream.
null
A dummy stream, output to it is discarded, on input it always gives end of file.

In a stand-alone ECLiPSe stdin, stdout and stderr are connected to the corresponding standard I/O descriptors of the process. In an embedded ECLiPSe, the meaning of stdin, stdout and stderr is determined by the ECLiPSe initialization options.

Current StreamDefault StreamStandard Stream
inputuser_inputstdin
outputuser_outputstdout
warning_outputuser_outputstdout
log_outputuser_outputstdout
erroruser_errorstderr
  null

Initial assignment of symbolic stream names

For compatibility with Prolog systems, the system accepts the stream name user in certain places. Its meaning is identical to stdin and stdout, depending on the context where it is used.

11.1.2  Stream Handles and Aliases

Streams can be identified by anonymous stream handles or by symbolic names.1 Most of the built-in predicates that require a stream to be specified have a stream argument at the first position, e.g., write(Stream, Term). This argument can be either a stream handle or a symbolic stream name.

Streams that are opened by programs should preferably use stream handles, as this allows the system to better keep track of their lifetime. Nevertheless, alias names can be given, either immediately when a stream is newly opened (e.g. with open/4), or later via redirection (set_stream/2). A stream can have more than one symbolic alias.

To obtain a handle when only an alias is known, use get_stream/2:

get_stream(StreamOrAlias, Handle)

get_stream/2 can also be used to check whether two stream names are aliases of each other.

Note that stream handles are not normal Prolog terms! They can not be assembled, decomposed, or occur literally in Prolog text.

11.1.3  Opening New Streams

Streams provide a uniform interface to a variety of I/O devices and pseudo-devices. The following table gives an overview of how streams on the different devices are opened.

I/O deviceHow to open
ttyimplicit (stdin, stdout, stderr) or open/3 of a device file
fileopen(FileNameModeStream)
stringopen(string(String), ModeStream)
queueopen(queue(String), ModeStream)
pipeexec/2, exec/3 and exec_group/3
socketsocket/3 and accept/3
nullimplicit (null stream)

How to open streams onto the different I/O devices

Most streams are opened for input or output by means of the open/3 or open/4 predicate. The goals

open(SourceSink, Mode, Stream)
open(SourceSink, Mode, Stream, Options)

open a communication channel with SourceSink.

If SourceSink is an atom or a string, a file is being opened and SourceSink takes the form of a file name in the host machine environment. ECLiPSe uses an operating system independent path name syntax, where the components are separated by forward slashes. The following forms are possible:

Note that path names usually have to be quoted (in single or double quotes) because they contain non-alphanumeric characters.

If SourceSink is of the form string(InitString) a pseudo-file in memory is opened, see section 11.3.1.

If SourceSink is of the form queue(InitString) a pseudo-pipe in memory is opened, see section 11.3.2.

Mode must be one of the atoms read, write, append or update, which means that the stream is to be opened for input, output, output at the end of the existing stream, or both input and output, respectively. Opening a file in write mode will create it if it does not exist, and erase the previous contents if it does exist. Opening a file in append mode will keep the current contents of the file and start writing at its end.

Stream is a symbolic stream identifier or an uninstantiated variable. If it is uninstantiated, the system will bind it to an anonymous stream handle:

[eclipse 1]: open(new_file, write, Stream).
Stream = $&(stream,7)
Yes (0.00s cpu)

If the stream argument is an atomic name, this name becomes an alias for the (hidden) stream number:

[eclipse 1]: open(new_file, write, new_stream).
yes.

This is equivalent to

[eclipse 1]: open(new_file, write, _, [alias(new_stream)]).
yes.

The stream identifier (symbolic or handle) may then be used in predicates which have a named stream as one of their arguments. For example

open("foo", update, Stream), write(Stream, subject), close(Stream).

will write the atom subject to the file ‘foo’ and close the stream subsequently.

It is recommended style not to use symbolic stream names in code that is meant to be reused. This is because these stream names are global, there is the possibility of name clashes, and the code will not be reentrant. It is cleaner to open streams with a variable for the stream identifier and pass the resulting handle as an argument wherever it is needed.

Socket streams are not opened with open/3, but with the special primitives socket/3 and accept/3. More details are in chapter 22.

A further group of primitives which open streams implicitly consists of exec/2, exec/3 and exec_group/3. They open pipe streams which connect directly to the I/O channels of the executed process. See chapter 21 for details.

11.1.4  Closing Streams

A stream lives until it is closed. Streams that are only referenced by handle are closed automatically, either on failure across the open/3,4 predicate, or after all copies of their handle become unused and garbage collected. This means that no extra precautions have to be taken to ensure that streams are closed on failure or when aborting. Handle-streams can optionally be closed explicitly if their lifetime is statically known in the program. Streams that have aliases cannot be closed automatically: all aliases must be closed explicitly.

The predicates close/1, 2

close(Stream)
close(Stream,Options)

are used to explicitly close an open stream. If a stream has several alias names, closing any of them will close the actual stream. All the other aliases should be closed as well (or redirected to streams that are still open), because otherwise they will continue to refer to an identifier of the already closed stream.

When an attempt is made to close a redirected system stream (e.g., output), the stream is closed, but the system stream is reset to its default setting.

11.1.5  Redirecting Streams

The set_stream/2 primitive can be used to redirect an already existing symbolic stream to a new actual stream. This is particularly useful to redirect e.g., the default output stream:

set_stream(output, MyStream)

so that all standard output is redirected to some other destination (e.g., an opened file instead of the terminal). Note that the stream modes (read/write) must be compatible. The redirection is terminated by calling

close(output)

which will reestablish the original meaning of the output stream by resetting it to the user_output default stream.

11.1.6  Finding Streams

The predicate

current_stream(?Stream)

can be used to backtrack over all the currently opened streams, and obtain handles for them (but not their aliases).

11.1.7  Stream Properties

A stream’s properties can be accessed using get_stream_info/3:

get_stream_info(+Stream, +Property, -Value)

e.g., its mode, line number, file name etc. Some stream properties can be modified using set_stream_property/3:

set_stream_property(+Stream, +Property, +Value)

e.g., the end-of-line sequence used, the flushing behaviour, the event-raising behaviour, the prompt etc.


1
Earlier ECLiPSe versions identified streams by small integers, which is now deprecated, except for some foreign language interfaces. If needed, the number is available as the physical_stream stream property.

Up Next