output ranges: by ref or by value?
Steven Schveighoffer
schveiguy at yahoo.com
Sat Jan 2 18:46:57 PST 2010
On Sat, 02 Jan 2010 19:51:41 -0500, Andrei Alexandrescu
<SeeWebsiteForEmail at erdani.org> wrote:
> Steven Schveighoffer wrote:
>> The function already exists -- opAssign. My point is that save would
>> just be the same as opAssign in most cases where you'd want to
>> implement save. Cases where you'd want to implement save differently
>> than opAssign are cases where you wouldn't want to use it in a generic
>> fashion.
>
> You might mean this(this).
Yes, that's what I meant :) I haven't gotten used to struct constructors.
> Either doesn't help because you'd still be unable to differentiate
> between input ranges and forward ranges. Much of the point of save() is
> to mark a syntactic difference between input ranges and forward ranges.
> Input ranges still need this(this) and opAssign - those just have
> different semantics.
That was the point of the enum. It would be a synthetic difference, but
IMO so is save. If it turns out that the only usable times you implement
save all look like:
auto save() { return *this; }
then save gives you nothing. It's kind of a proof by induction (I think).
You say that algorithm A requires the ability to save the state of a
range. So I define a function save on a range which cannot be saved by a
simple assignment (i.e. a file). I use A on that range, and the results
are not what I expect or kill performance or consume unneeded resources,
I'd rather not use algorithm A on that range, negating the need to define
a save function in the first place.
I think that's what we're going to end up with.
> A range that defines save() is a forward range. save() creates an
> independent range from its source. The file etc. example was
> hypothetical but realistic.
I meant what actual types of ranges, not categories, I don't know how to
say this better... i.e. the file range is one type, what are others?
What I'm looking for is ranges that would otherwise be input ranges unless
you define save on them. For example, a file range without save is an
input range, cannot be a forward range. But if you define save, it is a
forward range. An array is an example of a range that can be a forward
range without the save function.
My theory is, given this list of ranges, if you pair them with an
algorithm that requires save capability, you wouldn't want to use that
algorithm on it anyways (kinda like the consume example).
> Struct ranges won't work with a container hierarchy. If you define a
> container hierarchy (classes etc.) you'll also need a range hierarchy.
> Otherwise defining the inheritance relationship is impossible. Consider:
>
> abstract class Container(E) { // most general container
> @property bool empty();
> bool add(E element);
> E removeAny();
> InputRange!E opSlice();
> }
>
> That's what I'd expect any container worth its salt to implement: (a)
> test for empty; (b) add an element to the container; (c) remove some
> element from the container; (d) get a range that spans the entire
> container. (Probably removeAll() and a range-positioned remove() would
> make sense, too.)
The range interface is compile-time only, there is no need to define it in
the container interface. opSlice does not need to be part of the
interface, even if it is defined on all the container types.
opApply makes for a much better interface-friendly iteration anyways.
BTW, the primitives in dcollections are:
clear(); // clear all elements
remove(V v); // remove an element
contains(V v); // returns whether an element is contained in the colleciton
length(); // the length of the collection
dup(); // duplicate the collection
opApply(int delegate(ref V v) dg); // iterate the collection
opApply(int delegate(ref bool doPurge, ref V v) dg); // purge the
collection
That means it covers only empty in your list of must-have functions (via
length() == 0). Add is not a primitive because the Map collections
shouldn't assign some random key to the new element. removeAny is defined
only on sets and multisets, but I'm not sure that it couldn't be moved to
Collection, in fact, I'll probably do that.
Note that it's missing begin and end which are defined on every single
container type (i.e. the equivalent of the all-elements range). This is
because those primitives return a struct that is different for every
container type.
It also surpasses opSlice via opApply, since all an input range can do
anyways is iterate. In fact, opApply is more powerful because you can
change elements and remove elements (via purging). Plus it's more
efficient than a range-via-interface.
>> I see a range as being useful for iteration or algorithms, but not for
>> general use. A great example is AAs. Would you say that an AA *is* a
>> range or should *provide* a range? If it is a range, does that mean
>> you remove elements as you call popFront? Does that make any sense?
>> If it doesn't, then what happens if you add elements through another
>> alias to that AA?
>
> An AA provides several ranges - among which byKey and byValue.
I misunderstood your statement "[a container hierarchy] does need range
interfaces." I thought you meant containers themselves need to implement
the range interface, I see now that isn't the case, so my bad.
-Steve
More information about the Digitalmars-d
mailing list