Extending `getproperty`

So I want to extend getproperty (and likewise setproperty!) for a custom type, a bit like what LinearAlgebra.LU does. I see that this is usually done with an if/else chain; however, this is not very Julian in style, and moreover I would like to keep the list open if possible, and to do this for a lot of types without copying too much boilerplate code.

So I came up with the following solution, which allows me to add a custom property to a custom type in a one-liner if needed, at the cost of inserting some Val.

@inline getproperty(x, s::Symbol) = _getproperty(x, Val(s)) # <- ☠ type piracy, arrh!
@inline _getproperty(x, ::Val{S}) where{S} = Base.getfield(x, S)

# ...
function _getproperty(x::MyType, ::Val{:myfield})
  #...
end

The two first lines ensure that, for types for which I do not define _getproperty, this will be equivalent to the definition in Base.jl.

Is there anything wrong with this solution (apart from style points lost for type piracy)? Is there a special case which I missed? Will this compile to hundreds of specialized functions, or will it all be silently inlined? Are the @inline macros even useful here? Will this also work for setproperty!? Would it be better if I wrote a big @generated function looking if a method exist for _getproperty?

1 Like

Is there something in the example provided in the help of getproperty that does not work for you?

help?> getproperty
search: getproperty

  getproperty(value, name::Symbol)

  The syntax a.b calls getproperty(a, :b).

  Examples
  ≡≡≡≡≡≡≡≡≡≡

  julia> struct MyType
             x
         end
  
  julia> function Base.getproperty(obj::MyType, sym::Symbol)
             if sym === :special
                 return obj.x + 1
             else # fallback to getfield
                 return getfield(obj, sym)
             end
         end
  
  julia> obj = MyType(1);
  
  julia> obj.special
  2
  
  julia> obj.x
  1


1 Like

Well, you wouldn’t want to do it in a type-pirated way like that, but doing this same thing for specific types of x is somewhat common. So like make your original getproperty a method for x::MyType directly, not just for generic x. At least that’s what I do for ComponentArrays, but also with a @generated function for _getproperty. It seems to work better than anything else I tried. But there are a bunch of reasons you might want to use a @generated function. And also keep in mind that doing the Val thing can really slow things down if the compiler isn’t able to do constant propagation. Usually it’s fine for getproperty though.