On Sat, 02 Jan 2010 19:51:41 -0500, Andrei Alexandrescu <[email protected]> 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

Reply via email to