/PropertyUtils.jl

Properties in Julia made easy

Primary LanguageJuliaMIT LicenseMIT

PropertyUtils

This package lets you:

  1. Change getproperty to getfield via Fields(x)
  2. Change getproperty to getindex via Indexes(x) (most useful for AbstractDict{Symbol, Any}).
  3. Similarly, Fields, and Indexes change the behavior of setproperty!.
  4. Replace items in an expression with properties from a src via @with src expr.
  5. Join together properties of different objects via JoinProps.



@with

Replace items in an expression with properties from a src.

@with src expr
  • Every valid identifier x in expr gets changed to hasproperty(src, :x) ? src.x : x
z = 3
result = @with (x = 1, y = 2) begin
    x + y + z
end
result == 6



JoinProps

Join sources to create a union of their props.

Example:

a = (x = 1, y = 2)
b = (y = 3, z = 4)

j = JoinProps(a, b)

j.x == 1
j.y == 2  # non-unique props are taken from the first argument that has it
j.z == 4



Fields

Map getproperty to getfield.

struct A
    x::Int
end
Base.getproperty(::A, x::Symbol) = "hello!"

item = A(1)
f_item = Fields(a)

item.x == "hello!"
f_item.x == 1



Indexes

Map getproperty to getindex.

d = Dict(:x => 1, :y => 2)

Indexes(d).y == 2



Composability

@with, Fields, Indexes, and JoinProps play nicely together:

result = @with JoinProps(Fields(A(10)), a, b, Indexes(Dict(:twenty => 20))) begin
           x + y + z + twenty
       end

result == 36



How this all works with setproperty!

setproperty!, e.g. thing.x = 1, is supported if the underlying data structure supports mutation.

  • Indexes(x): setproperty! --> setindex!
  • Fields(x): setproperty! --> setfield!
  • JoinProps(x): setproperty! --> setproperty! on the first instance of the prop. You cannot create new props.
Indexes(d).z = 3

d[:z] == 3



Special Thanks

This package borrows ideas from StatsModels.jl, DataFramesMeta.jl, StaticModules.jl, and StatsPlots.jl, which are all fantastic.