Cost of public properties
-
In my view, the main advantage of Juliaâs multiple dispatch is that it allows everybody equal ability to define public functions of x::X
, rather than privileging the owner of X
. As a function author, I can write new functions that look and feel like they belong on x
just as much as X
âs author can.
Properties are similar to OOPâs X().f()
methods. They are in a namespace controlled by the author of X
. The symmetric extensibility permitted by multiple dispatch doesnât work nearly as well if properties are part of a public interface because they privilege a single argument and its getproperty
method.
A function author cannot define properties that look and feel native to X
without piracy.
-
It is useful when writing a package to think about the public interface separately from the implementation. Public properties reduce the delineation between interface and implementation, and may cause implementation details to leak into interfaces unnecessarily.
So public properties come at a significant cost.
Benefits of public properties
- At the definition site, using properties means authors donât have to write out the definition of the getters:
struct Person
name
age
end
is shorter than
struct Person
name
age
end
name(p::Person) = p.name
age(p::age) = p.age
-
At the call site, person.name
needs one fewer character than name(person)
.
-
At the call site, person.name |> length
puts the property on the right rather than the left of name(person) |> length
, so chains are read linearly in left-to-right order.
-
At the call site, if multiple packages in scope provide functions name
for their objects, they need to be used in package-namespaced form Persons.name(person)
or imported under a different name like using Persons: name as pname
, while person.name
does not.
-
At the call site, dot syntax can hint at certain information about a property access. Namely, person.name
indicates it does not require expensive computation, does not raise an error, and does not change its value unless otherwise mutated.
Analysis
Benefits 1,2,3 are issues of surface syntax that can be solved with a macro or operator, without paying Cost 1 of public properties.
For Benefit 1,
@getters struct Person
name
@nogetter age
end
could define name(p::Person) = p.name
without the user having to write it out.
For Benefit 2 and Benefit 3, various packages such as Chain.jl have been exploring chaining interfaces.
For example,
using Chain
struct Person
name
age
end
name(p::Person) = p.name
> person = Person("Alice", 99);
> @chain person name length
# 5
This chain is read linearly in left-to-right order (Benefit 3).
The chain doesnât require parentheses (Benefit 2) though it does require @chain
. The function-on-the-right invocation can also be written person|>name
in the same number of characters as name(person)
and one more than person.name
.
A bigger change would be making a property p
only accessible via function call p(x)
, and x.p
just a shortcut equivalent to @chain x p
. If desired, x.p
might automatically disambiguate to parentmodule(x).p(x)
if there is another p
in the calling scope. This would require experimentation to see where it causes breakage.
Benefit 4 (name collisions) remains but I believe it is small because the function import can be handled once at the top of the callerâs file.
Benefit 5 (property hinting) seems hardest to attain without dot-access properties, because hinting at those properties is essentially a form of documentation rather than a fact about the code. This could be done with prose documentation, or a form of code specification adjacent to each function definition.
Conclusions
Most of the advantages of public properties can be obtained in a functional API without the downsides. Some changes will make that easier:
-
Make it easier to define getters and setters. Promote the use of @getters
where appropriate and consider including it in Base.
-
Make it easier to chain calls. Promote |>
and Chain.jl.
-
For radicals duly cautioned, consider making a property p
only accessible via function call, and x.p
equivalent to @chain x p
.
-
Examine structured ways of specifying function properties like âconstant valueâ, âlow-cost accessâ, and ânon-erroringâ, and more.