Better documentation for properties vs. fields

Background

I found myself often using the quite uncomfortable

fieldnames(typeof(x))

Searching for something unrelated, I found in an old thread this magic:

propertynames(x)

Which turns out to be a fantastic alternative without so much boilerplate, and also the recommended way to override fieldnames functionality for those situations in which you want to keep attributes private, etc. The thing is, this is poorly documented.

How well documented are both alternatives

A quick search in the docs shows that fieldnames:

  • is referenced more times across the docs (8 times)
  • is referenced within “more important sources”, i.e., Essentials and Types pages
  • is referenced twice within devdocs, Reflection and ASTs

Whereas propertynames:

  • is less referenced (only 4 times)
  • only shows up in one “important” page, Essentials, which is the least self-explanatory source of information, because it is just a collection of Base

Final discussion

However, there is a more important point to make here, further than achieving more coverage for propertynames (quote for emphasis):

Properly documenting the distinction between properties and fields.

Several programming patterns depend upon exposing some fields of an struct and hiding others, or discouraging direct access to struct fields and rather use “getters”/“setters” (or any ad hoc method). This is the nature of the difference between fields and properties, and should be explained somewhere.

Final question

Can I propose a PR? Will it be welcome? Is this just me?

9 Likes

you can
you may
you should

5 Likes

Yes, absolutely! Documentation PRs are always very welcome, especially from users who may be fairly new to Julia, since it’s often difficult to judge for contributors which parts new users struggle with the most and where our documentation could still be improved.

3 Likes

Yes, better documentation would be great, and especially the intent of properties could be explained more. And yes, the distinction should be documented, and I currently fail to understand it. I thought of properties as an abstraction of fields, allowing for exposing and hiding things, and providing a property interface where the underlying fields could be changed without breaking behavior or the user knowing. However, the abstraction doesn’t always make sense:

julia> struct Creature number end

julia> dog = Creature(1);

julia> getfield(dog, :number)
1

julia> getproperty(dog, :number)
1

julia> fieldnames(typeof(dog))
(:number,)

julia> propertynames(dog)
(:number,)

julia> propertynames(Creature)
(:name, :super, :parameters, :types, :names, :instance, :layout, :size, :ninitialized, :hash, :abstract, :mutable, :hasfreetypevars, :isconcretetype, :isdispatchtuple, :isbitstype, :zeroinit, :isinlinealloc, :has_concrete_subtype, :cached_by_hash)

getfield and getproperty are roughly parallel, but not so for fieldnames and propertynames. Years ago you could do fieldnames(dog) but that was deprecated for reasons I don’t recall. But why should it be fine to do propertynames(dog), and why does propertynames(typeof(dog)) do what it does? That would be helpful to have explained in the documentation.

This is because properties may vary across instances of a given type, contrary to fields which appear in the type definition. For example, DataFrame columns can be accessed using getproperty, and column names of course vary from one data frame to another.

This is indeed worth documenting.

4 Likes

Alright then! I’m writing the issue and the PR :smiley:

2 Likes

Indeed, properties are a pretty good abstraction, and I prefer propertynames(dog) anyway. I guess the leakier abstraction was with fields, hence fieldnames(typeof(dog)). So @nandoconde, perhaps the appropriate thing is flip the emphasis: propertynames could be referenced 8 times, and fieldnames only 4! Thanks for raising the point, I think this is well worth improving.

If anyone wants to peep in, I have submitted it!

2 Likes