Defining custom getindex method

Hi all, just started learning Julia, and I think I’m missing something really obvious here. I’m trying to make a type indexable, but I get an error when using the [ …] syntax. Specifically, if g is an instance of my type, getindex(g,1) works fine, but g[1] gives me an error. The getindex I’ve defined seems like it should be suitably general to match this type, and this is especially weird to me because the documentation says that g[1] should be compiled as getindex(g,1). Example:

struct Graded{S,T}
    summands :: Dict{S,T}
end

function getindex(x::Graded{S,T}, k::S) where {S,T}
    return x.summands[k]
end

d = Dict{Int,Array{Int}}([0=>[], 1=>[1,2,3], 2=>[10,11,12]])
g = Graded{Int,Array{Int}}(d)

print(getindex(g,1))
print(g[1])

The last line gives the error:

 ERROR: LoadError: MethodError: no method matching getindex(::Graded{Int64,Array{Int64,N} where N}, ::Int64)

The problem here is that g[1] always calls Base.getindex, while your function definition has in fact made a new function in your current module. The solution is to always qualify functions you are trying to extend, when the original definition is in another module:

julia> @which getindex
Main

julia> methods(getindex)  # just one:
# 1 method for generic function "getindex":
[1] getindex(x::Graded{S, T}, k::S) where {S, T} in Main at REPL[523]:1

julia> methods(Base.getindex) |> length
517

julia> function Base.getindex(x::Graded{S,T}, k::S) where {S,T}
           return x.summands[k]
       end

julia> print(g[1])
[1, 2, 3]

It’s a little strange to think that you could mean anything else. But the general rule makes more sense – for instance it lets you use words like sum or sign as variable names within a function, so long as you assign to them before reading from them, without worrying that these also exist as functions exported by Base.

5 Likes

Perfect, thank you!