[m-dev.] for review: stream I/O
Peter Ross
petdr at miscrit.be
Mon Oct 2 20:38:38 AEDT 2000
On Mon, Oct 02, 2000 at 07:08:20PM +1100, Fergus Henderson wrote:
> On 01-Oct-2000, Peter Ross <petdr at miscrit.be> wrote:
>
> > +% File: stream.m.
> > +% Main author: petdr
> > +% Stability: exceptionally low.
> > +%
> > +% This file provides a typeclass for defining streams in Mercury.
> > +% It is completely pure and you are encouraged to use it to write
> > +% streams in Mercury. If however you are a library implementor then you
> > +% may want to look at the impure interface described in stream.impure.m
>
> I suggest s/want to/also want to/
>
> > + % Given a handle to a stream construct a unique stream object
> > + % which can be used to do IO on the stream.
> > + % This object initialises its state from the io__state.
> > +:- pred stream__init(S::in, stream(S)::uo, io__state::di, io__state::uo) is det.
> > +
> > + % Tie the stream back into the io__state. Omitting this call
> > + % may allow the compiler to optimize away the stream I/O.
> > +:- pred stream__destroy(stream(S)::di, io__state::di, io__state::uo) is det.
>
> There are some nasty semantic problems with these routines.
> At very least, the documentation is incomplete.
> More likely the interface declared here won't work (i.e. can't be
> implemented in a way that is logically sound and that matches
> their intended semantics).
>
> The comment for `stream__destroy' explains its purpose, but
> doesn't describe its effect. What are the declarative and
> operational semantics of `stream__destroy'?
>
> In the case where there is no call to stream__destroy, the logically
> correct behaviour is for there to be no side effects, except those caused
> by stream__init, and currently stream__init is documented (and
> implemented) in a way that seems to mean that it has no side effects.
> So the comment "Omitting this call may allow the compiler to optimize
> away the stream I/O" is not really correct -- in fact omitting this
> call may result in the implementation *inserting* I/O side effects
> that are not in the program's declarative semantics.
>
> One alternative would be to say that each call to stream__init
> must be matched by a corresponding call to stream__destroy
> and that if the call to stream__destroy is omitted, then the
> behaviour is undefined. After all, we already document that
> you get undefined behaviour in the case where you try to write
> to a stream that has already been closed, so why not here too?
>
> However, I wouldn't consider that acceptable. There's a fundamental
> difference between the two cases.
>
> For writes to closed files, an implementation can easily ensure that
> the behaviour is defined, e.g. throwing an exception, it's just that
> doing so comes at a performance or portability cost, so we don't want
> to require implementations to do so. For the same reason, we don't
> require array bounds checks for array__unsafe_lookup. For both writes
> to closed files and array__unsafe_lookup, the performance cost of
> doing the checks is only a constant, so implementations which need
> safety can easily do the checks. But it may be an important constant,
> so implementations which need maximum performance are not required to
> do the checks.
>
> In contrast, with this case, there is no easy way for a compiler to
> ensure soundness. Disabling the optimizations in question would
> only ensure *unsoundness*. In general, if a compiler using
> separate compilation sees
>
> foo(S0, S) -->
> stream__init(S0, S1),
> { write_string("hello\n", S1, S2) },
> bar(S2, S).
>
> then without seeing the body of bar/4, it can't know whether
> bar/4 calls stream__terminate, and so it can't know whether
> `write_string/3' should have any side effects.
>
> So, in summary, specifying that certain things result in undefined
> behaviour is IMHO OK only so long as it is feasible for an
> implementation to check whether those things occur and to provide a
> sound and well-defined behaviour in those cases, preferably with only
> constant-time overhead for the checking. I don't think that is the
> case here with missing calls to stream__terminate.
>
I understand the problem that you are pointing out here, and I would
appreciate a possible solution.
The only solution that I can think of is to have two stream type
classes, one which is side effect free and one which isn't.
Here is the interface for the side effect streams.
:- typeclass stream__input(S) where [
pred stream__read_char(S, stream__result(char), io__state, io__state)
mode stream__read_char(in, out, di, uo) is det
].
where S must be a handle which is used to select the correct read
character implementation.
Any other solutions?
> A related problem is with the ordering of I/O.
> The procedures here are all declared `det', but there's
> nothing which ensures that you don't try to open two
> different output streams to the same file or device
> and try to write to them at the same time. If you do,
> then the order of the output will be unspecified,
> and so you won't necessarily get the same result each time.
> Likewise, there is a problem with reading from a file
> at the same time as writing to it.
>
> There's two possible solutions that I can see.
> One is to ensure that files/devices get locked when you create
> input or output streams to them, and attempts to access a locked file
> from more than one stream at the same time (apart from multiple input
> streams, which should be allowed) result in I/O errors.
> The other is to make some of the procedures concerned `cc_multi'
> rather than `det'.
>
Currently there is nothing in io.m the prevents you from opening a file
twice for writing to it. Shall I change all those to cc_multi as well?
Pete
--------------------------------------------------------------------------
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