What's the difference between fields and properties?

I’m trying to understand this pull request which will be implemented in V0.7/V1.0:

https://github.com/JuliaLang/julia/pull/25311

I read the V0.7 documentation but I coudn’t find any clarification on the specific differences between fields and properties.

In fact the functions getfield and setfield! refer to getproperty and setproperty! but the latter functions are not fully documented. The main difference seems to be that field related functions are in Core and the property related functions are in Base. Could someone clarify?

Thanks

7 Likes

fields are simply the “components” of a struct. The struct

struct A
   b
   c::Int
end

has the fields b and c. A call getfield returns the object that is bound to the field:

julia> a = A("foo", 3)
A("foo", 3)

julia> getfield(a, :b)
"foo"

The dot syntax a.b used to “lower” i.e. be the same as writing getfield(a, :b). What has changed now is that a.b lowers to getproperty(a, :b) with the default fallback

getproperty(a::Type, v::Symbol) = getfield(a, v)

So by default, nothing has changed. However, authors of structs can hook into getproperty:

julia> function Base.getproperty(a::A, v::Symbol)
           if v == :c
               return getfield(a, :c) * 2
           elseif v == :q
               return "q"
           else
               return getfield(a, v)
           end
       end

julia> a.q
"q"

julia> getfield(a, :q)
ERROR: type A has no field q

julia> a.c
6

julia> getfield(a, :c)
3

julia> a.b
"foo"

So we can add extra functionality to the dot syntax (dynamically if we want). As a concrete example where this is useful is PyCall where you used to have to write pyobject[:field] while it is possible now to implement it such that you can write pyobject.field.

44 Likes

Great answer thanks. So in my updated mental model I think of property related functions as the mid-level abstraction between dot notation and field related functions.

edit: excited to start using this, it will be uber helpful for some things I need. Perfect timing :sunglasses:

2 Likes

would be great if some concise form of @kristoffer.carlsson 's answer could make it into the docstrings.

5 Likes

So you cannot overload getfield?

Indeed not.

julia> getfield
getfield (built-in function)
4 Likes

I think this follow-up question is probably directly related enough to the above that it is useful to have it attached to this thread but feel free (mods) to separate if you think it should be.

@kristoffer.carlsson in your great example you had logical branches which dealt with the cases of the different struct field symbols.

Is it possible to just specify the getproperty for just a single symbol without having the branching?

The syntax below, based on your example, hopefully explains what I have in mind (though it doesn’t work as is)
Base.getproperty(a::A, :q) = "q"

Not really, you would just have to write

Base.getproperty(a::A, v::Symbol) = v === :q ? "q" : getfield(a, v)
1 Like

You could consider something like

Base.getproperty(a::A, key::Symbol) = getproperty(a, Val{key}())
Base.getproperty(a::A, ::Val{K}) where K = getfield(a, K)
Base.getproperty(a::A, ::Val{:q}) = "q"

and count on constant folding (I didn’t check).

Also, maintaining a consistent propertynames may be tricky.

2 Likes