Transience of .front in input vs. forward ranges

Andrei Alexandrescu SeeWebsiteForEmail at erdani.org
Sun Nov 4 10:00:20 PST 2012


On 11/4/12 11:16 AM, H. S. Teoh wrote:
>> Fetching lines should be solved by using types; trafficking in
>> char[] does not offer guarantees about the preservation of the
>> content. In contrast, trafficking in string formalizes a binding
>> agreement between the range and its user that the content is
>> immutable.
> [...]
>
> I think that this may be oversimplifying the issue.

The express purpose here is to simplify instead of offering highly 
granular notions with many odd corner cases and combinations. So the 
risk of oversimplifying is indeed material.

> What about the
> example of a range of in-place permutations over an array? That range
> can be made a forward range, or even bidirectional (by reversing the
> sense of the comparison used by nextPermutation). But it is inherently a
> transient range.

That's an interesting example indeed. It would keep a T[] as its state 
and each popFront() would mutate that array to its next permutation. 
Implementing .save would be achieved by duplicating the array.

I would argue this is a tenuous range to characterize as a forward 
range. Invoking .save on a "usual" forward range means saving the 
iteration state, with an understanding that the saved range will go 
through the same iteration steps as the initial one. But nonono, 
actually the saved range goes through DUPLICATES of the states that the 
original goes through. Clearly there is an isomorphism of the two 
sequences of states, but they're not the same states - for example, 
someone looking at the original array will see the original changing the 
array, but not the .save.

So what you're looking at is a range that can define .dup, not one that 
can define .save.

> And this is merely one example. I have some non-trivial code that
> evaluates an expression tree, each node of which may generate multiple
> elements; one may take a range over all possible values that the tree
> may have. For efficiency reasons, each output value (which is
> vector-valued) reuses the same buffer. It is a forward range with a
> transient .front (it has to be a forward range, because otherwise one
> can't range over all combinations of multiple values that subtrees may
> produce).

I'm not sure about this example. Are we looking again at a .dup kind of 
thing?

> While I'm perfectly fine with the decision that such ranges shouldn't be
> considered proper ranges at all, it would greatly limit the
> applicability of std.algorithm in my code. Either I would have to
> artificially limit the range to an input range (which is a bit silly, as
> it amounts to renaming .save to something else just so std.algorithm
> wouldn't recognize it as a forward range), or I wouldn't be able to use
> std.algorithm to manipulate these ranges, but would have to roll my own.
> Of course, D makes it so that rolling my own isn't all that hard, but it
> would be a bit disappointing that the supposedly-generic algorithms in
> the standard library aren't that generic after all.

I understand. At the same time, there is a point where an abstraction 
can't comprehend all possible instances. There's already significant 
aggravation moveFront and hasLvalueElements etc., which I'd want to 
simplify given a chance. Continuing down that path seems risky to me.


Andrei


More information about the Digitalmars-d mailing list