[m-dev.] Stream2000

Fergus Henderson fjh at cs.mu.OZ.AU
Wed Nov 15 20:54:13 AEDT 2000


On 14-Nov-2000, Peter Ross <peter.ross at miscrit.be> wrote:
> 	%
> 	% The pure interface to streams.
> 	%
> :- typeclass stream(S) where [

I suggesting adding a blank line after that comment, and unindenting
the comment, since it applies to all the type classes that follow, not
just this one.

For this one, I'd put a comment

	% The root of the stream class hierarchy

> 		% A name describing the current stream.
> 	func stream__name(S) = string

I suggest s/name/human-readable name/
and appending "suitable for use in (e.g.) error messages.".

> :- typeclass stream__input(S) <= stream(S) where [
> 		% Read one character from the stream S.
> 		% Errors are reported via the stream__result type.
> 	pred stream__read_char(S::in, stream__result(char)::out,
> 			io__state::di, io__state::uo) is det
> ].
> 
> :- typeclass stream__output(S) <= stream(S) where [
> 		% Write one character to the stream S.
> 		% Throws a stream_error exception if a problem occurs.
> 	pred stream__write_char(S::in, char::in,
> 			io__state::di, io__state::uo) is det
> ].
>
> :- typeclass stream__duplex(S)
> 		<= (stream__input(S), stream__output(S)) where [].

I think it would help to have comments at the top of each of these
class declarations saying what each class represents, e.g.

	% Streams from which you can read input

	% Streams to which you can write output

	% Bidirectional streams

	% Streams with at least one character of pushback

> :- typeclass stream__putback(S) <= stream__input(S) where [
> 		% Putback one character on the input stream.
> 		% The implementation must guarantee at least one
> 		% character of putback and throw a stream_error
> 		% exception if a problem is encountered during the
> 		% putback.
> 	pred stream__putback_char(S::in, char::in,
> 			io__state::di, io__state::uo) is det
> ].

It might also be good to have

		% Streams with unbounded amounts of pushback
	:- typeclass stream__unbounded_putback(S) <= stream__putback(S) where [
		% This adds no new methods, just a guarantee:
		% there's no limit on the amount of putback except
		% available memory, so unless you run out of heap space,
		% the putback_char method must always succeed.

This will allow routines that require more than one character of
pushback (e.g. the current lexer) to document this in a way that the
compiler can help check it.

> %-----------------------------------------------------------------------------%
> 
> 	%
> 	% A input stream with infinite putback.
> 	%
> :- type putback(S).
> :- instance stream(putback(S)) <= stream(S).
> :- instance stream__input(putback(S)) <= stream__input(S).
> :- instance stream__putback(putback(S)) <= stream__input(S).
> 
> 	% Create the putback stream.
> :- pred putback_stream(S::in, putback(S)::out,
> 		io__state::di, io__state::uo) is det <= stream__input(S).

I think it would also be good to provide a way to get at the
underlying stream S from a putback(S) stream, e.g.

	:- func get_putback_base_stream(putback(S)) = S.

Otherwise, if I am passing around e.g. a putback(network_stream),
then I have no way of accessing the extra functionality associated
with network_streams, e.g. the network_stream__set_timeout_period method,
and so I'd have to pass around a pair(network_stream, putback(network_stream))
which would be rather ugly.

> %-----------------------------------------------------------------------------%
> 
> 	%
> 	% A stream which records which line of the input stream we are
> 	% up to.  Lines are numbered starting from one.
> 	%
> :- type linenumber(S).
> :- instance stream(linenumber(S)) <= stream(S).
> :- instance stream__input(linenumber(S)) <= stream__input(S).
> :- instance stream__putback(linenumber(S)) <= stream__putback(S).
> :- instance stream__line(linenumber(S)) <= stream__input(S).
>
> 	% Create the numbered stream.
> :- pred linenumber_stream(S::in, linenumber(S)::out,
> 		io__state::di, io__state::uo) is det <= stream__input(S).

Likewise here.

I don't think you need to repeat the "Lines are numbered starting from
one" comment here; the method declaration is the right place for it.

a/the numbered/a line-number counting/

> %-----------------------------------------------------------------------------%
> 
> % XXX When default type class implementations are introduced these
> % the following predicates should probably become members of the
> % relevant type classes.

s / When / If/when /
s / introduced these / introduced, /
s / the following predicates / some of the following predicates /

> % Predicates which require an input and a output stream.
> 
> 	% Echo stream S onto stream T.
> 	% Errors associated with stream S are reported through the
> 	% stream__res argument.  Errors associated with stream T throw a
> 	% stream_error exception.
> :- pred cat(S::in, T::in, stream__res::out,
> 		io__state::di, io__state::uo) is det
> 		<= (stream__input(S), stream__output(T)).

I suggest naming the two streams `InputS' and `OutputS'
rather than `S' and `T'.

The inconsistent error handling here is rather ugly.
For v2 of the streams library, it might be better to make
error handling for input streams the same as for output streams,
i.e. throw exceptions.  End-of-file should still be handled by
returning a discrinated union type, though.

> :- pred linenumber_read_char(linenumber(S)::in, stream__result(char)::out,
> 		io__state::di, io__state::uo) is det <= stream__input(S).
> 
> linenumber_read_char(line(Stream, MLine), Result) -->
> 	mutvar__take(MLine, Line),
> 	stream__read_char(Stream, Result),
> 	( { Result = ok('\n') } ->
> 		mutvar__put(MLine, Line + 1)
> 	;
> 		mutvar__put(MLine, Line)
> 	).

This is not as efficient as it could be in the non-multithreaded case.
I guess the difference is probably not hugely important, though.

> :- pred linenumber(linenumber(S)::in, int::out,
> 		io__state::di, io__state::uo) is det.
> 
> linenumber(line(_, MLine), Line) -->
> 	mutvar__take(MLine, Line),
> 	mutvar__put(MLine, Line).
>
> :- pred set_linenumber(linenumber(S)::in, int::in,
> 		io__state::di, io__state::uo) is det.
> 
> set_linenumber(line(_, MLine), Line) -->
> 	mutvar__take(MLine, _OldLine),
> 	mutvar__put(MLine, Line).

I think it would help to have `mutvar__peek'
and `mutvar__overwrite' operations that were defined as if by

	mutvar__peek(MutVar, Val) -->
		mutvar__take(MutVar, Val),
		mutvar__put(MutVar, Val).

	mutvar__overwrite(MutVar, Val) -->
		mutvar__take(MutVar, _OldVal),
		mutvar__put(MutVar, Val).

For the non-multithreaded case, these operations could avoid
the redunant load/store.

> % File: impure.m.
> % Main author: petdr
> % Stability: exceptionally low.
> %
> % An impure interface for describing streams.

See my review comments from last time around, in
<http://hydra.cs.mu.oz.au/mailing-lists/mercury-developers/mercury-developers.0010/0009.html>.

In particular:

	I would much prefer to have a pure stream.lowlevel.m that
	provides everything that stream.impure.m provides except the
	impurity.  I wouldn't mind having stream.impure.m so long as
	stream.lowlevel.m is there, but I object to having
	stream.impure.m in the public interface without
	stream.lowlevel.m being there, since it would encourage people
	to make unnecessary use of impurity.

I am not inclined to compromise on that one ;-)

-- 
Fergus Henderson <fjh at cs.mu.oz.au>  |  "I have always known that the pursuit
                                    |  of excellence is a lethal habit"
WWW: <http://www.cs.mu.oz.au/~fjh>  |     -- 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