On 01-Jun-2015 00:18, Mafi wrote:
On Sunday, 31 May 2015 at 17:50:41 UTC, Dmitry Olshansky wrote:
2. If we were to reuse algorithms - most algorithms love ForwardRange.
And there is a problem implementing it for streams in the _range_ API
itself.
Yeah, most streams are seekable like e.g. files or MM-files, so they
must be ForwardRanges... And indeed saving position is trivial.
Now look at the signature for ForwardRange:
struct Range{
...
@property Range save();
}
Yeah, you'd gotta duplicate the whole stream to count as ForwardRange.
Yicks! Signatures that might work are:
Mark save();
void restore(Mark m);
I think here you confuse typical Range semantics with the semantics of
the most prominent but also most akward Range implementation: dynamic
arrays. In my opinion the signature of 'save' for ForwardRanges is good.
I'm quite certain I'm talking of something beyond dynamic arrays -
buffered I/O much like D's std.stdio File wrapper of C's FILE I/O.
With most ranges the 'mark' of yours is already embedded inside the
range type and manipulated with 'popFront' (and 'popBack'). Other range
data is most probably stored with indirection so copying it is no harm.
As an example there is std.container.SList which is not a range. SList[]
returning SList.Range is. It references its SList without owning it and
therefore can be copied at will without touching the original SList.
Ranges were designed with algorithms and containers in mind. Current I/O
ranges were fitted in with a bit of strain and overhead.
I cannot think of a stream that deserves to be a ForwardRange but is
incompatible with this range pattern. Raw file handles cannot be
ForwardRanges because multiple aliasing ranges would cause incorrect
results.
The idea of reading raw file handles 1 byte at a time just to fit
InputRange interface is hilarious. What is an algorithm that *anyone* in
the right state of mind would want to run on raw file handle using such
interface?
Also it makes sense to accommodate a better set of primitives for raw
I/O, including support for scatter-gather read/write. And we wouldn't
have to constraining ourselves with stupid things like bool empty() -
that no one knows for sure until we read that file, pipe, socket...
On the other hand saving the state of a buffered stream is doable but
using the same type is unfortunate restriction.
>
Your proposed signatures wouldn't help either. An inefficient
file handle wrapper could be a correct ForwardRange if it seeks some
defined position before every read operation. But again this would mean
embedding this 'mark' inside the range.
I'm talking about buffered stuff obviously.
Also e.g. in memory-mapped file is trivial to save a position, and it
wouldn't require mapping all at once.
Speaking of InputRange as unbuffered stream, I'll refine my reply to
Andrei with more facts.
That range jacket simply doesn't fit:
- it requires buffering 1 element (front)
- requires primitives that are impossible to decently implement (e.g.
empty)
- convincingly suggest primitives that are anti-pattern in I/O: a
syscall per 1-element read? (at best)
Unbuffered I/O doesn't work like 3 separate things "peek, advance and
test for empty" troika. Otherwise I'm all for design by introspection
realization, it's just that I/O needs its own foundations.
--
Dmitry Olshansky