[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