[m-dev.] for discussion: stream library v2
Tyson Dowd
trd at cs.mu.OZ.AU
Tue Sep 26 17:10:01 AEDT 2000
On 25-Sep-2000, Peter Ross <peter.ross at miscrit.be> wrote:
> > >
> > > I think that the low level interface has to be impure
> >
> > Why?
> >
> because of the way it is defined. We define the type class method to read
> one character as follows
>
> :- impure pred read_char(Handle::in, Char::out) is semidet.
>
> This has to be impure because the implementation may be changing the
> external state of the world, it may also not be but we have to assume
> the worst.
>
> I for the life of me can think of a way to get around this in a clean
> and elegant manner.
I think the problem is that while the lowlevel impure interface is
fine for going straight to foreign language implementations, it should
not be the only way to implement a stream.
If I want to use the Mercury "string" type to implement string streams,
I should be able to do so in a pure manner. That is, I should be able
to be an instance of some stream interface that doesn't rely have the
word "impure" in it anywhere.
:- begin_wild_speculation_without_testing.
Perhaps the best way to do this is by having a second set of type
classes (we'll call them highlevel streams) that are intended for
implementing streams using pure building blocks. The methods would all
be pure and use di/uo moded stream pairs.
:- typeclass highlevel__output(S) where [
pred highlevel__output(char::in, S::di, S::uo) is det
].
The predicates that go here are basically the same as the ones in
stream.m (maybe a little bit different because they don't have to worry
about putback).
Any lowlevel can be an instance of highlevel -- just use a wrapper.
:- type lowlevel(T) ---> lowlevel(T).
:- instance highlevel__output(lowlevel(S)) <= lowlevel__output(S) where [
(highlevel__output(Chr, lowlevel(Stream), lowlevel(StreamOut)) :-
( impure lowlevel__write_char(Stream, Chr) ->
true
;
semipure Err = lowlevel__error_message(Stream),
throw(stream_error(Err))
),
StreamOut = Stream
)
].
The implementation of this might be different, whatever you decide as
the implementation of stream__write_char is basically the code that goes
here.
When you create a stream, if it uses the lowlevel interface to satisfy
stream, you need to create a wrapped version.
:- instance lowlevel(tcp).
:- instance lowlevel__input(tcp).
:- instance lowlevel__output(tcp).
:- instance lowlevel__duplex(tcp).
:- type tcp_stream == lowlevel(tcp).
Now tcp_stream is a member of highlevel. Note the wrapper is single
argument, single functor, so it won't actually involve an allocation.
So in summary:
1. Low-level impure interface.
:- typeclass lowlevel(S) where [ .... ].
2. High-level pure interface.
:- typeclass highlevel(S) where [ .... ].
3. All lowlevel are instances of highlevel.
:- instance highlevel(lowlevel(S)) <= lowlevel(S) where [
predicates to turn lowlevel stream handles into
di/uo stream paris
].
4. Stream type is highlevel + pushback capability.
:- type stream(S) ---> stream(S, list(char)).
Although it might be nice to make 4 a typeclass too, which adds the
operation putback_char, and all the other operations are the same as
a normal stream (but the read_char operation first looks for a putback
char).
Also, it might be worth renaming some of these types and typeclasses,
I've just picked arbitrary (perhaps confusing) names.
:- end_wild_speculation_without_testing.
--
Tyson Dowd #
# Surreal humour isn't everyone's cup of fur.
trd at cs.mu.oz.au #
http://www.cs.mu.oz.au/~trd #
--------------------------------------------------------------------------
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