Usage
Say we have a deeply nested struct:
julia> using StaticArrays;
julia> struct Person
name::Symbol
age::Int
end;
julia> struct SpaceShip
captain::Person
velocity::SVector{3, Float64}
position::SVector{3, Float64}
end;
julia> s = SpaceShip(Person(:julia, 2009), [0.0, 0.0, 0.0], [0.0, 0.0, 0.0])
SpaceShip(Person(:julia, 2009), [0.0, 0.0, 0.0], [0.0, 0.0, 0.0])
Lets update the captains name:
julia> s.captain.name = :JULIA
ERROR: type Person is immutable
It's a bit cryptic but what it means that Julia tried very hard to set the field but gave it up since the struct is immutable. So we have to do:
julia> SpaceShip(Person(:JULIA, s.captain.age), s.velocity, s.position)
SpaceShip(Person(:JULIA, 2009), [0.0, 0.0, 0.0], [0.0, 0.0, 0.0])
This is messy and things get worse, if the structs are bigger. Setfields
to the rescue!
julia> using Setfield
julia> s = @set s.captain.name = :JULIA
SpaceShip(Person(:JULIA, 2009), [0.0, 0.0, 0.0], [0.0, 0.0, 0.0])
julia> s = @set s.velocity[1] += 999999
SpaceShip(Person(:JULIA, 2009), [999999.0, 0.0, 0.0], [0.0, 0.0, 0.0])
julia> s = @set s.velocity[1] += 1000001
SpaceShip(Person(:JULIA, 2009), [2.0e6, 0.0, 0.0], [0.0, 0.0, 0.0])
julia> @set s.position[2] = 20
SpaceShip(Person(:JULIA, 2009), [2.0e6, 0.0, 0.0], [0.0, 20.0, 0.0])
Under the hood
Under the hood this package implements a simple lens api. This api may be useful in its own right and works as follows:
julia> using Setfield
julia> l = @lens _.a.b
(@lens _.a.b)
julia> struct AB;a;b;end
julia> obj = AB(AB(1,2),3)
AB(AB(1, 2), 3)
julia> set(obj, l, 42)
AB(AB(1, 42), 3)
julia> obj
AB(AB(1, 2), 3)
julia> get(obj, l)
2
julia> modify(x->10x, obj, l)
AB(AB(1, 20), 3)
Now the @set
macro simply provides sugar for creating a lens
and applying it. For instance
@set obj.a.b = 42
expands roughly to
l = @lens _.a.b
set(obj, l, 42)