Should the eachindex function be part of the Indexing interface?

Currently, the Indexing interface is composed of 4 functions:

image

However, among these functions, there is not one that returns the valid indexes of the indexable object.

In its documentation, it is shown that the eachindex function is related to AbstractArray. But I think it could be more generic and encompass all indexable objects.

Example

In this case, let’s use the same example from the documentation:

struct Squares
  count::Int
end

function Base.getindex(S::Squares, i::Int)
  1 <= i <= S.count || throw(BoundsError(S, i))
  return i*i
end

Base.firstindex(S::Squares) = 1
Base.lastindex(S::Squares) = S.count

squares = Squares(100)

With only these methods implemented, the eachindex function will not work:

julia> squares[50]
2500

julia> for s in eachindex(squares)
           println(s)
       end
ERROR: MethodError: no method matching keys(::Squares)
Closest candidates are:
  keys(::OrderedCollections.OrderedSet) at C:\Users\Dev01\.julia\packages\OrderedCollections\PRayh\src\ordered_set.jl:95
  keys(::Test.GenericArray) at C:\Users\Dev01\AppData\Local\Programs\Julia-1.8.1\share\julia\stdlib\v1.8\Test\src\Test.jl:1936
  keys(::Tuple) at tuple.jl:71
  ...
Stacktrace:
 [1] eachindex(itrs::Squares)
   @ Base .\abstractarray.jl:282
 [2] top-level scope
   @ .\REPL[2]:1

However, other indexable objects that are not subtypes of AbtractArray support the eachindex function, such as Dict:

julia> d = Dict(:a => 1, :b => 2)
Dict{Symbol, Int64} with 2 entries:
  :a => 1
  :b => 2

julia> for i in eachindex(d)
           println(d[i])
       end
1
2
2 Likes

Perhaps the function to extend is keys, and not eachindex. The latter seems to be an optimized version of keys for AbstractArrays, which falls back to keys for general indexed collections.

In fact, keys are more fundamental than firstindex and lastindex, as there might not be such a notion for an indexed collection, e.g. dictionaries. The documentation seems to suggest that the latter needs to be defined if one wants to index a collection with begin and end. Perhaps these may be listed as optional methods.

3 Likes

I don’t know if I agree @jishnub , it seems that the indexing interface in the docs is incomplete. If a type is indexable according to the described interface then the eachindex should work right? It seems that there is something off assuming that eachindex is a function that is supposed to work with any indexable.

1 Like

Somewhere there is a mention of regret on making keys extra special. From the help:

all valid indices for a arranged in the shape of a itself
Note that the keys of an array might not be the most
efficient index type; for maximum performance use eachindex.

  • keys are indices similarly, eachindex are indices iterably

imo RegexMatch would be better using NamedTuples (keys happened to be supported long before NamedTuples).

:+1:t3: eachindex an iterator that iterates relocating to Indexing