On Thursday, 6 February 2014 at 18:10:53 UTC, H. S. Teoh wrote:
Would it make sense to have a .full method/property analogous
to input ranges' .empty? Perhaps something like:
That could be ok but I agree that having to check is a pain.
Besides, what are you going to do if it is full? Suppose I call
char[1] b;
toUpper("lol", staticSink(b[]));
The best toUpper could do is truncate or throw.... and I think
that would be better encoded in the range itself so the caller
can decide.
auto got = toUpper("lol", staticSinkTruncating(b[]));
if(got.length < "lol".length) {
// we could perhaps call it again to process the rest
}
(put in the truncating one would just check its own length and
noop if it is full)
Otherwise, throwing range violations/out of memory exceptions are
what would most likely happen anyway.
One thing that
the current output range API doesn't do very well is chaining.
Indeed. In my other post, I just wrote about finish. Finish
serves to flush the buffer (digests or compression algorithms for
example might need to be padded to block size), could finalize
things (suppose an appender which just puts the pieces into a
static array, then calls join all at once at the end), and could
also just generally return the result.
There are some cases where returning from finish doesn't make
sense, such as if you sunk to a file, you wouldn't keep an array
of the contents around... but finish is still potentially useful
in that it could close the file or release a lock. (Of course,
dtors could do that too. But destructors can never return data to
the user - that's where finish is special.)
Anyway, not all output ranges would offer finish and not all
would return T[]. But not all input ranges offer opSlice either
so we're still in analogous territory.
This is a big usability hindrance. Ideally we'd want to write
something
like:
auto result = "mystring".toUpper(ArcOutputRange!string())
.translate("abc", "def");
But I'm not sure how this can be made to work.
hmmm.... finish doesn't account for all that.... well, I guess it
could by returning a range.
tbh toUpper might be better as a higher-order input range. Like
alias toUpper = map!charToUpper(...). Those chain, they don't
allocate, and they are well-defined right now.
Then at the end we build the result lazily and just put it all at
once into the output range.
"mystring".toUpper.translate("abc","def").array(ArcOutputRange!string());
Yeah, I actually think that's the way to go. And calling .array
at the end is nothing new to Phobos anyway. I'd be a bit weird
doing it with toUpper but I think it really is the best fit.
(BTW I would be PISSED of toUpper actually changed like this.
It'd break a bunch of code and I don't really care that toUpper
allocates. I want it to just work. But we could offer equivalent
functionality via per-character functions and map so we don't
have to break code to offer the new options.)
So we should extend put() to take an index, then?
that would work.
An allocator is definitely not an output range!
yup, and I don't think a static array is either. A static array
is neither an input range, since you can't do a = a[1..$]. But
offering easy getters for such is easy and it rox.
into a data sink should not care what an allocator is; they
should take an output range.
Actually, I think they should generate lazy input ranges whenever
possible. Then only at the end do we send it to the output range.
It's just input ranges aren't allowed to allocate, that would
kill their complexity guarantee, so we need an example of a
function which *must* allocate up front.
They want the random access output range. Otherwise we can just
put at the end.
Let
stdout do the buffering, and let toLower send the data to stdout
directly. Calling an allocator from toLower essentially amounts
to buffering the data twice.
yes
They should probably be *always* passed by ref, otherwise you
could end up with some pathological behaviour of data from
multiple sources overwriting each other because they were
operating on copies of output ranges instead of references to a
single one.
That won't necessarily work though, you can't have a ref default
parameter. But we can use pimpl or something to force a regular
struct to be a ref item. Lazy initialization can be surprising,
but we deal with that already with array slices so I think it is
ok.
Also, delegates and function pointers should be treated as
output ranges as well (Phobos should define .put and whatever
other needed methods for them via UFCS).
Yes, indeed.
Doesn't solve the case where you call some library function
that throws, though. :-(
at least there's nothrow if it is really that important to us.