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