Skip to content

Latest commit

 

History

History
153 lines (108 loc) · 4.58 KB

README.md

File metadata and controls

153 lines (108 loc) · 4.58 KB

Kaleido: some useful lenses

Stable Dev GitHub Actions Codecov Coveralls Aqua QA GitHub commits since tagged version

Kaleido.jl is a collection of useful Lenses and helper functions/macros built on top of Setfield.jl.

Features

Summary

  • Batched/multi-valued update. See @batchlens, MultiLens.
  • Get/set multiple and nested fields as a StaticArray or any arbitrary multi-valued container. See getting.
  • Get/set fields with different parametrizations. See converting, setting, getting.
  • Computing other fields during set and get; i.e., adding constraints between fields. See constraining.
  • Get/set dynamically computed locations. See FLens.

Batched/multi-valued update

Macro @batchlens can be used to update various nested locations in a complex immutable object:

julia> using Setfield, Kaleido

julia> lens_batch = @batchlens begin
           _.a.b.c
           _.a.b.d[1]
           _.a.b.d[3]  settingas𝕀
           _.a.e
       end;

julia> obj = (a = (b = (c = 1, d = (2, 3, 0.5)), e = 5),);

julia> get(obj, lens_batch)
(1, 2, 0.0, 5)

julia> set(obj, lens_batch, (10, 20, Inf, 50))
(a = (b = (c = 10, d = (20, 3, 1.0)), e = 50),)

(See below for what settingas𝕀 does.)

Get/set multiple and nested fields as a StaticArray

It is often useful to get the values of the fields as a vector (e.g., when optimizing a composite object with Optim.jl). This can be done with getting(f) where f is a constructor.

julia> using StaticArrays

julia> lens_vec = lens_batch  getting(SVector);

julia> @assert get(obj, lens_vec) === SVector(1, 2, 0.0, 5)

julia> set(obj, lens_vec, SVector(10, 20, Inf, 50))
(a = (b = (c = 10.0, d = (20.0, 3, 1.0)), e = 50.0),)

Get/set fields with different parametrizations

Kaleido.jl comes with lenses settingasℝ₊, settingasℝ₋, and settingas𝕀 to manipulating fields that have to be restricted to be positive, negative, and in [0, 1] interval, respectively. Similarly there are lenses gettingasℝ₊, gettingasℝ₋, and gettingas𝕀 to get values in those domains. The naming is borrowed from TransformVariables.jl.

julia> lens = (@lens _.x)  settingasℝ₊;

julia> get((x=1.0,), lens)  # log(1.0)
0.0

julia> set((x=1.0,), lens, -Inf)
(x = 0.0,)

Kaleido.jl also works with AbstractTransform defined in TransformVariables.jl:

julia> using TransformVariables

julia> lens = (@lens _.y[2])  setting(as𝕀);

julia> obj = (x=0, y=(1, 0.5, 3));

julia> get(obj, lens)
0.0

julia> @assert set(obj, lens, Inf).y[2]  1

It also is quite easy to define ad-hoc converting accessors using converting:

julia> lens = (@lens _.y[2]) 
           converting(fromfield=x -> parse(Int, x), tofield=string);

julia> obj = (x=0, y=(1, "5", 3));

julia> get(obj, lens)
5

julia> set(obj, lens, 1)
(x = 0, y = (1, "1", 3))

Computing other fields during set and get

It is easy to add constraints between fields using constraining. For example, you can impose that field .c must be a sum of .a and .b by:

julia> obj = (a = 1, b = 2, c = 3);

julia> constraint = constraining() do obj
           @set obj.c = obj.a + obj.b
       end;

julia> lens = constraint  MultiLens((
           (@lens _.a),
           (@lens _.b),
       ));

julia> get(obj, lens)
(1, 2)

julia> set(obj, lens, (100, 20))
(a = 100, b = 20, c = 120)

Notice that .c is updated as well in the last line.

Get/set dynamically computed locations

You can use FLens to get and set, e.g., the last entry of a linked list.