opIndex() may hide opSlice()
H. S. Teoh via Digitalmars-d
digitalmars-d at puremagic.com
Fri Mar 10 10:43:43 PST 2017
On Fri, Mar 10, 2017 at 07:41:31AM -0800, Jonathan M Davis via Digitalmars-d wrote:
> On Friday, March 10, 2017 14:15:45 Nick Treleaven via Digitalmars-d wrote:
> > On Friday, 10 March 2017 at 01:10:21 UTC, H. S. Teoh wrote:
[...]
> > > Using opSlice() for slicing (i.e., arr[]) is old,
> > > backward-compatible behaviour.
> >
> > This seems non-intuitive to me (at least for single dimension
> > containers) - when you see var[], do you think var is being
> > indexed or do you think var is being sliced like an array
> > (equivalent to var[0..$])?
>
> Yeah, I've never understood how it made any sense for opIndex to be
> used for slicing, and I've never used it that way.
It's very simple, really. Under the old behaviour, you have:
arr[] ---> arr.opSlice()
arr[x] ---> arr.opIndex(x)
arr[x..y] ---> arr.opSlice(x,y)
This made implementing higher-dimensional slicing operators hard to
define, especially if you want mixed slicing and indexing (aka
subdimensional slicing):
arr[x, y] ---> arr.opIndex(x, y)
arr[x, y..x] ---> ?
arr[x..y, z] ---> ?
arr[w..x, y..z] ---> arr.opSlice(w, x, y, z) // ?
Kenji's insight was that we can solve this problem by homogenizing
opSlice and opIndex, such that [] *always* translates to opIndex, and ..
always translates to opSlice.
So, under the new behaviour:
arr[] ---> arr.opIndex()
arr[x] ---> arr.opIndex(x)
arr[x,y] ---> arr.opIndex(x,y)
arr[x..y] ---> arr.opIndex(arr.opSlice(x,y))
arr[x, y..z] ---> arr.opIndex(x, arr.opSlice(y,z))
arr[x..y, z] ---> arr.opIndex(arr.opSlice(x,y), z)
This allows mixed-indexing / subdimensional slicing to consistently use
opIndex, with opSlice returning objects representing index ranges, so
that in a multidimensional user type, you could unify all the cases
under a single definition of opIndex:
IndexRange opSlice(int x, int y) { ... }
auto opIndex(I...)(I indices)
{
foreach (idx; indices) {
static if (is(typeof(idx) == IndexRange))
{
// this index is a slice
}
else
{
// this index is a single index
}
}
}
Without this unification, you'd have to implement 2^n different
overloads of opIndex / opSlice in order to handle all cases of
subdimensional slicing in n dimensions.
So you can think of it simply as:
[] == opIndex
.. == opSlice
in all cases.
It is more uniform this way, and makes perfect sense to me.
> I generally forget that that change was even made precisely because it
> makes no sense to me, whereas using opSlice for slicing makes perfect
> sense. I always use opIndex for indexing and opSlice for slicing just
> like they were originally designed.
[...]
This is probably why Kenji didn't deprecate the original use of opSlice,
since for the 1-dimensional case the homogenization of opSlice / opIndex
is probably unnecessary and adds extra work for the programmer: if you
want to implement arr[x..y] you have to write both opSlice and an
opIndex overload that accepts what opSlice returns, as opposed to just
writing a single opSlice.
So probably we should leave it the way it is (and perhaps clarify that
in the spec), as deprecating the "old" use of opSlice in the
1-dimensional case would cause problems.
T
--
Chance favours the prepared mind. -- Louis Pasteur
More information about the Digitalmars-d
mailing list