On Tue, 09 Oct 2012 11:52:29 -0400, monarch_dodra <monarchdo...@gmail.com>
wrote:
On Tuesday, 9 October 2012 at 15:27:44 UTC, Steven Schveighoffer wrote:
On Tue, 09 Oct 2012 09:39:32 -0400, monarch_dodra
<monarchdo...@gmail.com> wrote:
On Tuesday, 9 October 2012 at 13:22:28 UTC, Steven Schveighoffer wrote:
[SNIP]
I tend to disagree with your examples, because, you are mixing the
notion of run-time failure with logic error.
They are one and the same.
Putting into a file that runs out of disk space, and putting into an
array that runs out of memory.
I'm not convinced.
A file running out of memory is an implementation defined limitation
that is out of the field of control of the developer, just as much as an
OutOfMemoryError.
An array that runs out of memory is predictable logic error. The problem
is that we aren't giving the developer the tools required to predict it.
predictable logic errors == assert
I'm not against defining a standard way to say "I'm full", but
proposing it *can't* say that is not the solution. Clearly, we could
do better in defining a standard way to test for fullness (full
property akin to empty?). Even so, putting into a non-full range
could generate an error.
Hum... I'm just kind of wondering here: Couldn't we simply have put
throw an actual exception? Something along the lines of
"OutputRangFullException"? That would be a pretty good compromise.
I think it would work, but I think we still need a way to check for
fullness.
Here is what I propose:
OutputRange is defined as an entity that consumes data. If you put
data into an OutputRange that cannot accept the data, the range has the
option of asserting or throwing an exception.
TerminatingOutputRange is an extension of OutputRange, but defines bool
@property full(). R.full returns true if it cannot accept any new
data. It should assert if you try to put data into a full
TerminatingOutputRange. In other words the following sequence should
always assert or not compile:
static assert(isTerminatingOutputRange!(typeof(r)));
assert(r.full);
r.put(x);
If you try and put into a TerminatingOutputRange that is *not* full,
behavior reverts to OutputRange (can either assert or throw an
exception), depending on the assumptions that can be made for that
condition.
I'll have to try to sleep on this before making any
judgements/thoughts/comments.
But off the top of my head, you'll still run into the same problem of an
output range becoming full *during* a put: if r accepts a T, then it
accepts an input range of T.
OK, I see your point, you need to know "can I put x into this output
range" instead of "can I put an element into this output range".
We are delving at this point into streams, and streams have a much better
interface for that:
int write(x)
where the int returned is how much data from x was actually written.
As put doesn't return anything, there is no way to tell what was written.
I don't know if it can be changed at this point.
No, my problem is not one of "first call", it is one of answering not
empty, but choking on a put(element) afterwards.
*Me "outputRange, are you an output range or int[] ?"
*outputRange: "Yes"
*Me: "outputRange are you empty?"
*outputRange: "No"
*Me: "then put this int[] _element_"
*outputRange: "OutOfRangeError"
*Me: "WTF?"
To me, this is not acceptable behavior.
Neither is requiring output ranges to be infinite. There are definitely
finite output ranges.
Note that you are not asking the right question "are you empty?" This is
an input range property, not an output range property. There is no
equivalent output range property.
And as you point out, this question necessarily has to be worded in a way
that is clear. "are you full?" would be a property that says "there is
enough space left for at least one more element", and "can you accept x
elements" would be an entirely different question.
But even if we *had* the right functions to ask those questions, finding
the answer may not be feasible (e.g. no length property).
I would say we need a new function, like tryPut or something, that returns
the number of elements actually put.
Another solution could be something closer to my very first proposal of
tightening the valid *ElementTypes* that are compatible with an output
range (but not put itself).
For example, a delegate D that accepts a T (like a char) would be
defined as return true to:
isOutputRange!(D, T) //true
isOutputRange!(D, T[]) //true
isOutputRange!(D, T[][]) //true
An actual inputRange!T (IR) (such as int[]) that defines empty, though,
would only be an output range for EXACTLY T:
isOutputRange!(IR, T) //true
isOutputRange!(IR, T[]) //false
isOutputRange!(IR, T[][]) //false
This would nip the problem in the bud, as empty would *really* mean
empty. If R says he's an outputRange of T, but not of T[], then don't
trust it to not overflow if you feed it a T[]...
No, not really. The only correct (and efficient) way to fix this is to
support partial writes.
As for the delegates, well they don't have empty anyways, so you can go
ahead and attempt to cram anything you want.
Then you have accomplished nothing.
void foo( int[] x)
{
int[5] y;
uint filled = 0;
put((int n) { y[filled++] = n; }, x);
}
-Steve