[m-dev.] for discussion: stream library v2

Fergus Henderson fjh at cs.mu.OZ.AU
Thu Sep 28 02:34:18 AEDT 2000


On 27-Sep-2000, Peter Ross <petdr at miscrit.be> wrote:
> :- type lowlevel(S) ---> lowlevel(S).

Is `S' here a state, or a handle?
(See below for what I mean by this.)

For the high-level interface `S' is clearly a state type,
not a handle type, but for the low-level interface I'm confused.

> :- typeclass lowlevel(S) where [
> 		% Did an error occur processing the stream?
> 	semipure pred lowlevel__is_error(S::ui, string::out) is semidet
> ].
> 
> :- typeclass lowlevel__input(S) <= lowlevel(S) where [
> 		% Read one character from the stream described by S.
> 		% Fail if we reach eof or some error condition.
> 	impure pred lowlevel__read_char(S::ui, char::out) is semidet,
> 
> 		% Have we reached the eof for S?
> 	semipure pred lowlevel__is_eof(S::ui) is semidet
> ].
> 
> :- typeclass lowlevel__output(S) <= lowlevel(S) where [
> 		% Read one character from the current stream.
> 	impure pred lowlevel__write_char(S::ui, char::in) is semidet
> ].

Why do you use `ui' modes here?

One serious problem with all that is that `ui' modes are not supported yet.

So, at least for the short term, I think you should change all of
those so that instead of `S::ui' they have `S::di, S::uo',
and make that argument pair last so that we can use DCG notation,
and change them from semidet to det with an extra `bool::out'
parameter.

If you do that, then I think a nice side-effect is that the interface
will be pure (at least if `S' is a state type rather than a handle type),
so you can also drop the "semipure" and "impure" annotations.

Another alternative would be to just leave out the lowlevel interface
for now, and thus require everyone to use the high-level interface.

> 	% The state of the world for one stream of type S.
> :- type stream(S).

The comment here is not clear; what do you mean
"the state of the world for one stream"?
Do you just mean "the state of a stream"?

> :- type stream(S)
> 	--->	stream(
> 			S,		% Handle on the stream
> 			list(char)	% Putback characters
> 		).

The comment here is also confusing; is `S' a handle to the stream,
or is `S' the stream state (excluding putback chars)?

Normally "handle" is used to mean an abstract kind of pointer or
reference.  A "handle" would not need to be uniquely moded, in
general, since you modify what the handle refers to, rather than the
handle itself.

If the idea is that stream(S) is a state but S is a handle,
then you need an additional opaque field in the stream(S) type,
e.g. one of type c_pointer.  Otherwise consider the problems
that could arise if e.g. some predicate operating on streams
is tabled, or if some nifty compiler optimization makes the same kind
of assumptions as tabling does.  Reading a character from a handle
doesn't change the handle, and won't change the putback characters
if they were already empty, but it had better change the stream(S).

> 	% Given a handle to a stream construct a unique stream object
> 	% which can be used to do IO on the stream.
> 	% This object initialises its state of the world from the
> 	% io__state. XXX This isn't such a good idea for string streams,
> 	% but is for other streams.
> :- pred stream__init(S::in, stream(S)::uo, io__state::di, io__state::uo) is det.

OK, here you seem to be assuming that `S' is a handle and `stream(S)'
is a state type.  But what happens if you call `stream__init' twice in
a row on the same handle?  Won't you get two handles to the same state?
And then won't updates to the two handles occur in an unspecified order?

If the intent is to treat this as if it were a form of concurrency,
then it should be cc_multi, not det.

Here's an example of what I'm talking about:

	:- pred nasty(Handle::in, io__state::di, io__state::uo) <= highlevel__output(S).
	nasty(Handle) -->
		stream__init(Handle, StreamA0),
		stream__init(Handle, StreamB0),
		{ stream__write_string("foo\n", StreamA0, StreamA1),
		  stream__write_string("bar\n", StreamB0, StreamB1) }.

There's two side effects here: which side effect happens first?
If you don't use --strict-sequential, the compiler could reorder
the two calls to stream__write_string, or even interleave them.
Note that we could also use parallel conjunction (&) inside the curlies there.

Hmm, that raises another problem: what about the singleton variables
`StreamA1' and `StreamB1' in this example?  If these streams are I/O
streams, whose updates have externally visible side effects, don't you
need some way to tie those variables back into the final io__state
returned from main?  Otherwise the compiler could just optimize away
all the stuff in curlies, since it is det and has no output variables
except the singletons, whose scope is local to that goal.

> :- module stdio.
> 
> :- interface.
> :- import_module highlevel. 
> 
> :- type stdio.
> :- type stdio_stream == lowlevel(stdio).

Here you refer to `lowlevel', but you didn't import that module.

Ideally you should make `stdio_stream' an abstract type,
and the `stdio' type should not be exported at all,
it should only occur in the interface.
Beware though that you aren't allowed to have instance
declarations that refer to abstract types which are defined
as equivalence types.

> :- module main.
> :- interface.
> :- import_module io.
> :- pred main(io__state::di, io__state::uo) is det.
> 
> :- implementation.
> :- import_module stdio, stream.
> :- import_module list.
> 
> main -->
> 	io__write_string("Hello world.\n"),
> 
> 	{ stdio_stream(StdioStream) },
> 	stream__init(StdioStream, Stream),
> 	{ generic_io(Stream, _) }.
> 
> :- pred generic_io(stream(S)::di, stream(S)::uo) is det <= highlevel__output(S).
> 
> generic_io -->
> 	stream__write_string("Hello world from stream library.\n").

With `--no-fully-strict' (which should perhaps be the default, but isn't),
the compiler will optimize away the call to generic_io/2 here, since it
has no outputs.

-- 
Fergus Henderson <fjh at cs.mu.oz.au>  |  "I have always known that the pursuit
WWW: <http://www.cs.mu.oz.au/~fjh>  |  of excellence is a lethal habit"
PGP: finger fjh at 128.250.37.3        |     -- the last words of T. S. Garp.
--------------------------------------------------------------------------
mercury-developers mailing list
Post messages to:       mercury-developers at cs.mu.oz.au
Administrative Queries: owner-mercury-developers at cs.mu.oz.au
Subscriptions:          mercury-developers-request at cs.mu.oz.au
--------------------------------------------------------------------------



More information about the developers mailing list