Convenient overloading of setproperty!
for lightweight reactive structs.
This package exports the macro
Annotating a (mutable) struct definition with @computed
facilitates the definition of fields (dependent variables), which are automatically computed when an instance is created. If the struct is declared as mutable
, the corresponding dependent fields are re-computed whenever an independent variable (field) is updated. Care is taken to ensure the correct order of computations.
The macro defines
- The struct (e.g.
TheType
) with field types as annotated. - A constructor
TheType(indep_vars...)
- (
mutable
only) A methodcomputeproperty!(::TheType, dep_var::Symbol)
that recomputes the fielddep_var
. The computation propagates and triggers re-computations downstream in the computational graph by default. Setpropagate=false
if it should not. - (
mutable
only) A methodsetproperty!(::TheType, indep_var::Symbol, value)
that setsfield
tovalue
and triggers computation of dependent variables.
julia> @computed struct SinCos
x::Float64
thesincos::Tuple{Float64,Float64} = sincos(x)
end
julia> sc = SinCos(pi/2)
SinCos(1.5707963267948966, (1.0, 6.123233995736766e-17))
## Mutable
julia> @computed mutable struct MSinCos
x::Float64
thesincos::Tuple{Float64,Float64} = sincos(x)
end
julia> sc = MSinCos(0.0)
MSinCos(0.0, (0.0, 1.0))
julia> sc.x = pi/2
1.5707963267948966
julia> sc.thesincos
(1.0, 6.123233995736766e-17)
# trying to set a computed field errors
julia> sc.thesincos = (0.0, 0.0)
ERROR: cannot set calculated field thesincos
[...]
Parametric types are supported:
julia> @computed mutable struct VectorAndNorm{N,T}
v::SVector{N,T}
norm::T = LinearAlgebra.norm(v)
end
julia> vec_and_norm = VectorAndNorm(@SVector [1.0,2.0,3.0])
VectorAndNorm{3, Float64}([1.0, 2.0, 3.0], 3.7416573867739413)
julia> vec_and_norm.norm
3.7416573867739413
-
It's the user's responsibility to make sure no circular dependencies amongst fields are introduced.
-
Computed fields must be explicitly type annotated, or they default to
Any
. -
Re-computations are triggered by mutating fields. Thus, e.g.
@computed mutable struct VectorMax v::Vector{Float64} max::Float64 = maximum(v) end vm = VectorMax([1.0,2.0,3.0]) # vm.max == 3.0 vm.v[1] = 10.0 # vm.max is _not_ 10.0 now # call computeproperty!(vm, :max) explicitly instead.
does not work.
-
Updating multiple independent fields simultaneously is not (yet) supported.
-
Because an inner constructor is automatically defined, you cannot provide your own.
- Support immutable struct: setting an independent field returns a new instance.
- Multi-update
- Propagating re-computation of dependent fields.