Overloading 'getproperty' for arrays with custom element types

Okay so I wanted to make some quick ways to get data from an array of custom types, but I need some advice to avoid ambiguous method errors.

So I have code that does something like this (note that you need to define at least two methods , so for two different custom element types):

struct MyStruct{T}
    value::T
end
function Base.getproperty(vector::Vector{<:MyStruct}, s::Symbol)
    if s == :value
        return [x.value for x in vector]
    elseif s in fieldnames(Vector)
        return getfield(vector, s)
    else
        error("cannot retrieve $s from Vector{<:MyStruct}")
    end
end

struct MyOtherStruct{T}
    another_value::T
end
function Base.getproperty(vector::Vector{<:MyOtherStruct}, s::Symbol)
    if s == :another_value
        return [x.another_value for x in vector]
    elseif s in fieldnames(Vector)
        return getfield(vector, s)
    else
        error("cannot retrieve $s from Vector{<:MyOtherStruct}")
    end
end

I just want this for some syntactic sugar, like:

julia> [MyStruct(5), MyStruct(7)].value
2-element Vector{Int64}:
 5
 7

I might also want to define this for SubArray and in general just AbstractArray, but let’s just work with Vector as an example for now.

So everything was working fine. But then I got an error with AlgebraOfGraphics v0.10.2, which called into Dictionaries v0.4.5, basically doing this:

using Dictionaries
p = pairs(NamedTuple())
dictionary(p)

This calles into sizehint! with an empty vector, as such:

empty_vector = Vector{Union{}}()
sizehint!(empty_vector , 8)

And now I get ambiguous errors, because sizehint! calls empty_vector.ref. So that’s the minimum working example, just doing getproperty(Vector{Union{}}(), :ref):

ERROR: MethodError: getproperty(::Vector{Union{}}, ::Symbol) is ambiguous.

Candidates:
  getproperty(vector::Vector{<:MyOtherStruct}, s::Symbol)
    @ Main Untitled-1:19
  getproperty(vector::Vector{<:MyStruct}, s::Symbol)
    @ Main Untitled-1:10

Possible fix, define
  getproperty(::Vector{Union{}}, ::Symbol)

I don’t want to define that method getproperty(::Vector{Union{}}, ::Symbol) in my packages, that would clearly be type piracy.

So I’m wondering if my code is wrong. Or should I just not want to do this? Or should I open an issue on the Julia language github to solve this method ambiguity explicitly?

1 Like

I think this might already be in pirate territory but you can try to change your signatures to

function Base.getproperty(vector::Vector{MyStruct{T}}, s::Symbol) where {T}
2 Likes

Yeah that does solve it for the case of vectors containing the same parametric types, but what if I want to work with other types as well?

Using the method

function Base.getproperty(vector::Vector{MyStruct{T}}, s::Symbol) where {T}

This is the result:

julia> [MyStruct(5.0), MyStruct(6.0)].value
2-element Vector{Float64}:
 5.0
 6.0

julia> [MyStruct(5.0), MyStruct(5)].value
ERROR: type Array has no field value
Stacktrace:
 [1] getproperty(x::Vector{MyStruct}, f::Symbol)
   @ Base .\Base.jl:49
 [2] top-level scope
   @ Untitled-1:30

But I get the point, I’ll see if I can limit my implementation to an even more specific combination of parametric types. Or maybe it’s time to remove that syntactic sugar.

This is likely in the territory of piracy. The “proper” way to do this is to create a custom <: AbstractArray type that supports such operations. It looks like you want to implement a Struct Of Arrays, in which case StructArrays.jl probably already covers your usecase.

1 Like

Make you own struct <: AbstractArray and overload for that. Overloading for Array{T} itself will not be good.