Daniel Keep wrote:
Looking at the documentation, I found std.range.isForwardRange.  I had a
look at the implementation, and it basically just checks to make sure
you can do this:

R r1;
R r2 = r1;

How do you construct an input range which is not also a forward range?

In fact you can't. Thank you for opening this discussion, it's very interesting.

In the current system, an input range and a forward range cannot be distinguished statically. I wanted to, and initially I thought it's possible, but I had to concede defeat. The problem s that forward ranges also need to be copyable, otherwise using them is a pain. Then I considered defining different symbols for input vs. other ranges, but that only makes things harder.

So right now... input range means "one pass, can't save iteration state and backtrack" and forward range means "forward pass, can save iteration state".

Aside from making it possible to forbid assignment (blech), it seems
like there needs to be a way to flag a range as not supporting resuming.
 The only reasonable way I can think of off the top of my head would be
something like this:

template isResumableRange(R)
{
    enum bool isResumableRange = isInputRange!(R)
        && (is(typeof(R.isResumableRange)) && R.isResumableRange)
        && is(typeof(
        {
            R r1;
            R r2 = r1;
        }()));
}

unittest
{
    static assert(!isResumableRange!(int));
    static assert(isResumableRange!(int[]));

    struct A {}
    struct B
    {
        void next();
        bool empty();
        int head();
    }
    struct C
    {
        static enum isResumableRange = true;
        void next();
        bool empty();
        int head();
    }
    struct D
    {
        static enum isResumableRange = false;
        void next();
        bool empty();
        int head();
    }
    static assert(isResumableRange!(A));
    static assert(isResumableRange!(B));
    static assert(isResumableRange!(C));
    static assert(isResumableRange!(D));
}

I also took the opportunity to rename isForwardRange to isResumableRange
since I can't see how the ability to make a "checkpoint" has anything to
do with being able to go forward.  For example isBidirectionalRange
requires isForwardRange, but what does saving checkpoints have to do
with being able to go in both directions?

So essentially we're looking at a symbolic approach - a resumable range would need to advertise that. I've used that for isSorted too, and it works pretty well.

The remaining question is one of defaults - are most ranges resumable or not? I.e., should a range advertise explicitly when it's resumable or when it's *not* resumable? I think the safe approach is to go as you say: a range ain't resumable unless it explicitly claims to. So if you forget stuff, the compiler will remind you.

I'd love to hear more opinions on the topic.


Andrei

Reply via email to