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