[m-rev.] for review: stream typeclasses

Julien Fischer juliensf at csse.unimelb.edu.au
Mon Oct 23 18:12:21 AEST 2006


On Mon, 23 Oct 2006, Peter Ross wrote:

>> ** Limitations in the current implementation of typeclasses currently
>>    make this less useful than it might initially appear.
>>
> What limitations?

* type variables in instance declarations must be wrapped in functors.
* duplicate type variables cannot occur in instance declarations.

We also ran into problems with range restrictedness condition for
fds but Mark is fixing that.

...

>> Index: library/stream.m
>> ===================================================================
>> RCS file: library/stream.m
>> diff -N library/stream.m
>> --- /dev/null	1 Jan 1970 00:00:00 -0000
>> +++ library/stream.m	23 Oct 2006 04:59:46 -0000
>> @@ -0,0 +1,353 @@
>
> [snip]
>
>> +%-----------------------------------------------------------------------------%
>> +%
>> +% Input streams
>> +%
>> +
>> +    % An input stream is a source of data.
>> +    %
>> +:- typeclass stream.input(Stream, State, Error)
>> +    <= ( stream(Stream, State), stream.error(Error), (Stream -> Error) )
>> +    where
>> +[
>> +    % For buffered input streams this method causes the buffer
>> +    % to be filled.  For unbuffered streams it is a no-op.
>> +    %
>> +    pred fill(Stream::in, State::di, State::uo) is det
>> +].
>> +
> Why do you need a fill predicate?
>
> If streams are managing there own buffer, I can't see how it can be to
> the users benefit to request the buffer to be filled, unless the filling
> of the buffer is an asynchronous operation.

It was intended to be useful in a similar sense to the way map.optimize
was useful (at least before it was redefined as a no-op).  If the
programmer know that a lot of stuff is about to be read in then they
can froce the buffer to fill itself (without having to read anything
from the stream) in anticipation of that.

> If the stream doesn't manage it's own buffer, then shouldn't one
> have an is_input_buffer_empty predicate?
>
> Anyway more justification of this is needed for me.

...

>> +    % A reader stream is a subclass of specific input stream that can be
>> +    % used to read data of a specific type from the input stream.
>> +    % A single input streams can support multiple reader subclasses.
>> +    %
>> +:- typeclass stream.reader(Stream, Unit, State, Error)
>> +    <= stream.input(Stream, State, Error) where
>> +[
>> +    % Get the next unit from the given stream.
>> +    % The get operation should block until the next unit is available.
>> +    %
>> +    pred get(Stream::in, stream.result(Unit, Error)::out,
>> +        State::di, State::uo) is det
>> +].
>> +
> I think streams should support non-blocking I/O as well.

So do I.

> I can't forsee any problems due to the current hierachy of typeclasses
> to supporting non-blocking I/O, but I haven't thought about it deeply.
> Have you?

Yes.  I had two approaches in mind but hadn't made my mind up which to
choose.  I guess it depends on how much of a distinction you want to
make between blocking and non-blocking streams.

[1] Extend the stream.result type as follows:

 	:- type stream.result(T, Error)
 		--->	ok(T)
 		;	eof
 		;	unavailable	% or whatever name you prefer.
 		;	error(Error).

The put and/or get methods of non-blocking streams would return
unavailable in the case where there was nothing to read or write.
This approach is a little awkward because blocking streams then need
to deal with the unavailable result.

[2] Change the stream.result type as above but retain the requirement
     that put/4 and get/4 always block.  Introduce two new typclasses
     as follows:


 	:- typeclass non_blocking_reader(...)  <= ... where
 	[
 		pred poll(Stream::in, Unit::out, State::di, State:uo)
 		is det.
 	].

 	:- typeclass non_blocking_writer(...) etc.

     We then add:

 	:- inst stream.blocking.result
 		--->	ok(ground)
 		;	eof
 		;	error(ground).

      and change the signature of get to:


 	:- pred get(Stream::in, Unit::out(blocking_result), State::di,
 		State::uo) is det.

      That way blocking streams don't have to worry about handling the
      unavailable result, which would be useful since blocking streams
      are likely to be the more common of the two kinds.

...

>> +%-----------------------------------------------------------------------------%
>> +%
>> +% Output streams
>> +%
>> +
>> +    % An output stream is a destination for data.
>> +    % Note that unlike input streams, output stream do not include
>> +    % an explicit error type.  They should handle errors by throwing
>> +    % a exceptions.
>> +    %
>
> s/a //g

Fixed.

...

>> +% Putback streams
>> +%
>> +
>> +    % A putback stream is an input stream that allows data to be
>> +    % pushed back onto the stream.  As with reader subclasses it is
>> +    % possible to define multiple putback subclasses for a
>> +    % single input stream.
>> +    %
>> +:- typeclass stream.putback(Stream, Unit, State, Error)
>> +    <= stream.reader(Stream, Unit, State, Error) where
>> +[
>> +    % Un-gets a unit from the specified input stream.
>> +    % At least one unit of can be placed back on the stream.
>> +    %
>> +    pred unget(Stream::in, Unit::in, State::di, State::uo) is det
>> +].
>
> I would reword that to say only one unit of putback is guaranteed to be
> successful.

Done.

...

> The only issue that I can think of is to do with the issue that you have
> a di/uo state pair.  Does this mean that there is going to be a problem
> with nested uniqueness?

Maybe.  The reason for having a di/uo state pair is that we definitely
need to support the I/O state as a stream state.  There was an example
on the stream branch of using strings as the state which did work (until
Mark fixed the duplicate type variables in instance decls check).

> For a concrete example, an encrypt/decrypt stream which is layered on
> top of another stream.

I suspect you going to bump into the above-mentioned limitations with
the typeclass system if you want it such a stream to be parameterized
over the state type (at least for now); if the state is specific one
such as the I/O state or a store it should be fine.

Julien.
--------------------------------------------------------------------------
mercury-reviews mailing list
Post messages to:       mercury-reviews at csse.unimelb.edu.au
Administrative Queries: owner-mercury-reviews at csse.unimelb.edu.au
Subscriptions:          mercury-reviews-request at csse.unimelb.edu.au
--------------------------------------------------------------------------



More information about the reviews mailing list