I am playing around with a custom named array and trying to figure out how to make it play nicely with custom indices, such as those in @mbauman’s fantastic InvertedIndices package.
Here’s a toy example, simple as I could make it:
# A struct representing a named index
struct Name{T}
name::T
end
# A toy "array with names" wrapping a data array
struct Arr{T, N} <: AbstractArray{T, N}
data::Array{T, N}
end
# Standard array functions and indexing
Base.size(A::Arr) = size(A.data)
Base.axes(A::Arr) = axes(A.data)
Base.getindex(A::Arr, i::Int) = A.data[i]
Base.getindex(A::Arr{T, N}, I::Vararg{Int, N}) where {T, N} = A.data[I...]
# Toy named index lookup; resolve names to the name they wrap
resolve_index(A, i) = i
resolve_index(A, i::Name) = i.name
That defines the basic array. The core problem comes up here:
function Base.to_indices(A::Arr, inds, I::Tuple{Any, Vararg{Any}})
I′ = Tuple(resolve_index(A, i) for i in I)
to_indices(A.data, inds, I′)
end
# Indexing with names and more complicated indices, e.g. :
function Base.getindex(A::Arr{T, N}, I::Vararg{Any}) where {T, N}
# Resolve any names to indices into the underlying array
I′ = to_indices(A, I)
A.data[I′...]
end
Note: The
{Any, Vararg{Any}}
part of theTuple
type is needed to disambiguate from thisto_indices
method in Base.
Here’s where the trouble comes in:
# You may need to `]add https://github.com/mbauman/InvertedIndices.jl`
using InvertedIndices
a[Not(2)] # => error
That fails with a method ambiguity error:
MethodError: to_indices(::Arr{Int64,2}, ::Tuple{Base.OneTo{Int64}}, ::Tuple{InvertedIndex{Int64}}) is ambiguous. Candidates:
to_indices(A, inds, I::Tuple{InvertedIndex,Vararg{Any,N} where N})
in InvertedIndices at …/packages/InvertedIndices/Ulzlz/src/InvertedIndices.jl:68to_indices(A::Arr, inds, I::Tuple{Any,Vararg{Any,N} where N})
in Main at In[7]:25 (note: this is my version above)
Possible fix, define
to_indices(::Arr, ::Any, ::Tuple{InvertedIndex,Vararg{Any,N} where N})
The big goal is to be able to use both names and Not
s together without my package needing to be aware of InvertedIndices:
a[Not(Name(2)), :]
I asked Matt B. about this earlier today and this iteration is based on his feedback. My understanding is that the dispatch chain has to go from my getindex
method → to_indices
in InvertedIndices → to_indices
implemented above for Arr
→ to_indices
from Base on the underlying data array, but I haven’t quite been able to set it up.
What can one do to have custom arrays and custom indices happily coexist?
P. S. Here’s a gist with all of the code in one piece.