[m-dev.] streams proposal (part 2)

Ian MacLarty maclarty at cs.mu.OZ.AU
Fri Feb 24 06:33:12 AEDT 2006


On Tue, Feb 21, 2006 at 05:48:29PM +1100, Julien Fischer wrote:
> 
> I've been playing around with streams proposal originally posted by Ian here:
> 
> <http://www.cs.mu.oz.au/research/mercury/mailing-lists/mercury-developers/mercury-developers.200601/0012.html>
> 
> Attached are an extension of Ian's original proposal (streams.m) extended so
> that most of the functionality for text streams in the standard library can be
> described using streams and a module showing how text streams in the
> standard library can be made instances of the stream typeclasses
> (stream_io.m).
> 
> Some caveats:
> 	- these module are not really documented, but hopefully the
> 	  intention should be obvious
> 	- there are quite a few design alternatives that have been left
>           in but commented out
> 	- quite a few functional dependencies are missing from sub-class
> 	  decls.
> 
> Comments welcome.

...

> %-----------------------------------------------------------------------------%
> %
> % Stream errors
> %
> 
> :- type stream.name == string.
> 
> :- type stream.result(T)
>     --->    ok(T)
>     ;       eof
>     ;       some [Error] error(Error) => stream.error(Error).
> 
> :- typeclass stream.error(Error) where
> [
>     % Convert a stream error into a human-readable format.
>     % e.g. for use in error messages.
>     %
>     func error_message(Error) = string
> ].
> 

I don't think this is a particularly useful way to do errors.  In my opinion
it would be better to add an extra Error argument to the stream typeclass and
make Error functionally dependent on Stream.  That way you can do more
with a returned error than simply converting it to a string (since you'll
know its ground type).

> %-----------------------------------------------------------------------------%
> %
> % Streams
> %
> 
>     % The root of the stream class hierarchy.
>     %
> :- typeclass stream(Stream, State)
>     <= (Stream -> State) where [
>     
>     % A human readable name describing the stream.
>     %
>     pred name(Stream::in, stream.name::out, State::di, State::uo) is det,
> 

I would prefer this to be

    func name(Stream) = string,

but for the moment it has to have the State, because io.input_stream needs to
read the I/O state to access the stream info database.

>     % Close the stream.
>     % Throws an exception if there is an error while closing the stream.
>     %
>     pred close(Stream::in, State::di, State::uo) is det
> ].
> 

I don't think close should be a method of the typeclass.  I think different
stream types may require different close methods (with different numbers
and/or types of arguments) or no close method at all.
Typically the operations of opening a stream and closing
a stream will go together and since we are not including opening of
streams in the typeclass I don't think we should include closing of
streams either.

> %-----------------------------------------------------------------------------%
> %
> % Input streams
> %
> 
> % :- typeclass stream.input(Stream, State) <= (Stream -> State) where [].
> % 
> % :- typeclass stream.input(Stream, Unit, State)
> %          <= stream.input(Stream, State) where [
> %   pred get(Stream::in, stream.result(Unit)::out, State::di, State::uo) is det
> % ].
> 
> :- typeclass stream.input(Stream, Unit, State)
>         <= stream(Stream, State) where [
>     pred get(Stream::in, stream.result(Unit)::out, State::di, State::uo) is det
> ].
> 
> %-----------------------------------------------------------------------------%
> %
> % Output streams
> %
> 
> % :- typeclass stream.output(Stream, State) <= (Stream -> State) where [].
> % 
> % :- typeclass stream.output(Stream, Unit, State)
> %         <= stream.output(Stream, State) where [
> %     pred put(Stream::in, Unit::in, State::di, State::uo) is det
> % ].
> 
> :- typeclass stream.output(Stream, Unit, State)
>         <= stream(Stream, State) where [
>     pred put(Stream::in, Unit::in, State::di, State::uo) is det
> ].
> 
> %-----------------------------------------------------------------------------%
> %
> % Duplex streams
> %
> 
> :- typeclass stream.duplex(Stream, Unit, State)
>         <= (stream.input(Stream, Unit, State),
>             stream.output(Stream, Unit,State)) where [].
> 
> %----------------------------------------------------------------------------%
> %
> % Putback streams
> %
> 
> :- typeclass stream.putback(Stream, Unit, State)
>         <= stream.input(Stream, Unit, State) where [
>     
>     pred unget(Stream::in, Unit::in, State::di, State::uo) is det
> ]. 
> 

How would unget work?  What happens if I get an 'a', but then unget a 'b'?

> :- typeclass stream.unbounded_putback(Stream, Unit, State)
>     <= stream.putback(Stream, Unit, State) where [].
> 
> %----------------------------------------------------------------------------%
> %
> % Buffered streams
> %
> 
> % If we want to force buffered streams to be output only then use this
> % one and uncomment stream.output/2.
> %
> %:- typeclass stream.buffered(Stream, State)
> %        <= stream.output(Stream, State) where [
> %    pred flush(Stream::in, State::di, State::uo) is det
> %].
> 
> :- typeclass stream.buffered(Stream, State)
>         <= stream(Stream, State) where [
>     pred flush(Stream::in, State::di, State::uo) is det
> ].
> 
> %----------------------------------------------------------------------------%
> %
> % Seekable streams
> %
> 
> :- type stream.whence
>     --->    set
>     ;       cur
>     ;       end.

Should the set function symbol have an argument?

> 
>     % XXX call this random_access?
>     %
> :- typeclass stream.seekable(Stream, State)
>         <= stream(Stream, State) where [
>     pred seek(Stream::in, stream.whence::in, int::in, State::di, State::uo)
>         is det
> ].
> 
> %----------------------------------------------------------------------------%
> %
> % Line oriented streams
> %
> 
> :- typeclass stream.text(Stream, State)
>         <= stream(Stream, State) where [
>     pred get_line(Stream::in, int::out, State::di, State::uo) is det,
>     pred set_line(Stream::in, int::in,  State::di, State::uo) is det
> ].
> 
> %-----------------------------------------------------------------------------%
> 
> % It would probably also be useful to have something like the following.
> 
> :- typeclass stream.standard_reader(Stream, Unit, State) 
>         <= ( stream.input(Stream, Unit, State),
>              stream.buffered(Stream, State),
>              stream.text(Stream, State)) where [].
>     
> :- typeclass stream.standard_writer(Stream, Unit, State)
>         <= ( stream.output(Stream, Unit, State),
>              stream.putback(Stream, Unit, State),
>              stream.text(Stream, State)) where [].
> 
> %-----------------------------------------------------------------------------%
> :- end_module stream.
> %-----------------------------------------------------------------------------%

One possible problem with the proposal is you can't have the notion of a
current open stream (like we do in io.m).  This is because you can't get
enough information from the stream alone to know what typeclass it
belongs to (you need the unit type too), so you can't store the stream
value in, say, a global mutable and then later extract it and use it.

Ian.
--------------------------------------------------------------------------
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