On Wed, 06 Oct 2010 19:09:15 -0400, Andrei Alexandrescu <[email protected]> wrote:

On 10/6/10 12:50 CDT, Steven Schveighoffer wrote:
I agree all of this except for save() (not a secret to anyone who's read
my posts).

What was your alternative design that obviates save()?

First, let me discuss why I don't like save.

Let's classify ranges into two types. Primitive ranges are ones that provide a range interface to data that otherwise does not have a range interface. A good example is a container range.

Compound ranges are ranges that combine or alter range functionality from a wrapped range or set of ranges. A good example is Zip or Chain.

Let's also define the trivial save implementation as a single line: return this;

We can assume that compound ranges don't contain a trivial solution, but only because the range(s) they contain could implement a non-trivial solution. So we can qualify those trivial, even though they don't have the canonical trival solution.

Most ranges that are not compound implement the trivial solution. This includes all dcollections ranges and *ALL* non-compound ranges in phobos.

Then there are ranges which cannot implement save whatsoever, even if they wanted to. A range that provides the range interface for a network stream would be a good example.

That leaves us with ranges which cannot implement the trival solution, but can implement save. A class-based range would be a good example. Currently, there are zero examples of this type of range. That's right, zero (grep 'save()' std/*.d). I contend that we have no place for such ranges in D.

So my question is, what is the point of save? The whole point is for this last class of ranges, so they can implement a way to copy the iteration position in a way that isn't doable via simple assignment. But there *AREN'T ANY* of these ranges in existence. Why do we have a feature that is littered all over phobos and any range that wants to be more than a basic imput range when the implementation is return this; ?

Now, this isn't quite fair -- save is important not only for what it provides but for what it excludes. It correctly excludes the ranges that cannot implement it. And these types of ranges do exist. So if we get rid of save, we have to compensate for this as well.

--------
So now you know why I don't like save.  I'll move on to my alternative.

My solution is this. The class of ranges that cannot implement the trivial save will define an enum refIterator to be true. And that's it. It shouldn't hurt currently since no ranges implement a non-trivial save. If we come across a range where it makes sense to implement save, we can figure that out later. But our solution works easily in phobos:

size_t bringToFront(Range1, Range2)(Range1 front, Range2 back)
    if (isForwardRange!Range1 && isForwardRange!Range2)

Look, there's already a template constraint for isForwardRange. Just define isForwardRange to disqualify ranges which define the refIterator enum, and we have to change no code (except get rid of all the save litter).

I'll also note, another unsolved issue with this is iteration --
iterating a sealed range via foreach is not going to use moveFront.

Well it shouldn't because the user doesn't want to destroy the stuff is iterating.

Yeah, but I also don't think by default he wants to make an arbitrary-cost copy.

Imagine a situation like this:

foreach(i; arr)
{
   if(i > 5)
     return i;
}

If arr is an array of BigInt, you are copying all of them out of the array, just to read them.

All I'm saying is moveFront simply solves the swapping problem, there are other problems with arbitrary cost copy construction that are not solved by moveFront.

Andrei wrote:
4. It would be a definite departure from C++, where all value copies
are considered of arbitrary cost. This would provide a convenient
straw-man for naysayers (e.g. "Hey, D calls the copy constructor even
more often than C++! No thanks, I'll stick with C++0x which solves it
all with rvalue references").

Wait, shouldn't auto ref solve the rvalue references thing? I mean if
you are copying an rvalue, there is no reason to keep the original
around, so no need to call the copy constructor, right?

auto ref has its uses, but designing an interface with front() but not moveFront() will make it impossible to move the front out of the range.

But your point was about how C++ has rvalue references, how does rvalue references solve the moveFront problem? FWIW, I thought C++ supports passing rvalue references only by const, no?

-Steve

Reply via email to