"IndexType" for ranges

monarch_dodra monarchdodra at gmail.com
Tue Oct 2 06:17:58 PDT 2012


If you've ever worked on a template that needs to index a range, 
you may have run into this problem: What is the type you should 
use to index an RA range?

The problem may not sound like much, but it is a royal pain in 
the ass when trying to write "wrapper ranges", such as 
std.algorithm.map.

Shoot to low, and you may en up failing to compile code such as 
"r[r.length - 1]", because r.length is of type ulong, making the 
implicit down-cast illegal.

Shoot to high, and the implementation itself will fail to compile:
auto opIndex(ulong n)
     return r[n]; //cannot cast ulong to size_t

You might think "just use typeof(length)" BUT:
*you aren't even guaranteed that "typeof(length)" will be 
correct! Certain ranges, such as iota, will return a length 
usually of type uint, but be indexed with ulong... :/
*Infinite ranges don't have length...

--------
I'd like to propose a trait called "IndexType" or "IndexingType". 
This trait would be defined as "A type that can be used to safely 
index (or slice) a range".

Here is how I would define and implement this trait:
"If R is a RandomAccessRange, then IndexType is the type used by 
opIndex".

Simple enough, and I got it done and working locally, but there 
are 2 issues I'd like to share and inquire here:

*First, if R also verifies hasLength, then writing "r[r.length]" 
should be a compile time legality: The type returned by Length 
must fit inside opIndex. This might sound obvious, but this 
restriction is .
**I propose adding this extra restriction to isRandomAccess: "if 
the range verifies $(D hasLength), then it must also be 
index-able by the type returned by length"

*Second, the idea is that IndexType should *also* be useable to 
slice a range. Because of this, I'd like to add two extra 
restrictions to isSliceable:
**A sliceable range must also be indexable(RA). A range that is 
sliceable but not indexeable is kind of retarded anyways, since 
you can index doing r[n, n+1].front;
**Given that R must be indexable, the type used to index the 
range must be compatible with slicing.

--------
These are not big changes I'm proposing, but they *may* break 
some existing ranges. Those ranges are arguably retarded, and 
these changes would enforce correctness, but they'd break none 
the less. I'd like some feedback if you think this trait is worth 
pushing?

--------

For illustration, here are three examples:
//--------
struct S1
{
   //Other primitives here...
   @property ushort length();
   auto opIndex(uint);
   auto opSlice(ulong, ulong);
}
struct S2
{
   //Other primitives here...
   @property ulong length();
   auto opIndex(ushort);
}
struct S3
{
   //Other primitives here...
   @property ushort length();
   auto opIndex(ulong);
   auto opSlice(ushort, ushort);
}
//--------
Here:
*S1 would have a "IndexType" equal to uint. S1 would be a 
RandomAccessRange and verify isSliceable.
*S2 would NOT be a random access range, because its length can't 
index it.
*S3 would be a RandomAccess. it's IndexType would be ulong. It 
would not verify "hasSlicing" because it can't be sliced using 
IndexType.


More information about the Digitalmars-d mailing list