Thanks for all answers and sorry for the late reply.
At first I tried creating new overloads semi-ignored the HSlice problem.
Semi-ignored because now the '..' and related operators from system always
return a HSlice instead of a slice, even if both sides are regular indexes. So
I changed the type to accept HSlice[SomeInteger, SomeInteger], but not the
implementation. This satisfied me for a while, but now I came back to implement
the BackwardsIndex for slices and the previous overload solution simply doesn`t
scale well.
I then used jypayne link to see how that change was made in the standard
library, and found the ^^ helper template, that is indeed very useful,
especially with the following concept:
type SomeIndex = SomeInteger | BackwardsIndex
template `^^`(s, i: untyped): untyped =
(when i is BackwardsIndex: s.len - int(i) else: int(i))
Run
Using them updating the functions is just a matter of updating the index/bounds
type and prepending s^^ (or whatever the name of your first argument) to every
use of the index/bounds type. No overloading needed, just two functions for
indexing (and one for slicing):
proc `[]`*[T](s: MemView[T], i: SomeIndex): var T {.inline.} =
## Mutable array like access of element ``i``.
xBoundsCheck(s, s^^i)
result = s.data[s^^i]
proc `[]=`* [T] (s: MemView[T], i: SomeIndex, val : T) {.inline.} =
## Change element ``i`` of the view.
xBoundsCheck(s, s^^i)
s.data[s^^i] = val
Run
I think there is no need for defining a indexing one with non var T result,
right?
And the example usages were also interesting. I always thought about hash table
indexes as a "key", so it did not come to my mind this problem. Those
infinite/sparse structures can indeed have problems with backwards indexing.
And on the other hand, the linked list that has slow general indexing, but fast
last and first element indexing, can also benefit from this change, as jyapayne
showed.