diff --git a/src/sugar.jl b/src/sugar.jl index ca3e742d..6709d8f2 100644 --- a/src/sugar.jl +++ b/src/sugar.jl @@ -275,3 +275,63 @@ function show_composition_order(io::IO, lens::ComposedOptic) print(io, ")") end + +""" + decompose(optic) + +decomposes the `ComposedOptic` into tuple of simple properties (Lenses) +while preserving the order + +```julia +julia> decompose(@optic _.a.b.c.d) +((@optic _.d), (@optic _.c), (@optic _.b), (@optic _.a)) + +julia> decompose((@optic _.c.d) ∘ (@optic _.a.b)) +((@optic _.d), (@optic _.c), (@optic _.b), (@optic _.a)) +``` +""" +decompose(optic) = (optic,) +decompose(optic::ComposedOptic) = (decompose(optic.outer)..., decompose(optic.inner)...) + +""" + propname(::PropertyLens{name}) + +returns the name of the propery lens +```julia +julia> propname(@optic _.a) +:a +``` + +Using `propname` with `decompose`, list all keys of can be listed as +```julia +julia> map(propname, decompose((@optic _.c.d) ∘ (@optic _.a.b)) +(:d, :c, :b, :a) +``` +""" +propname(::PropertyLens{name}) where {name} = name + + +""" + normalise(optic) + +transforms the optic such that the `inner` lens contains always a the `PropertyLens` + +```julia +julia> optic = (@optic _.c) ∘ (@optic _.a.b) +(@optic _.c) ∘ (@optic _.b) ∘ (@optic _.a) + +julia> optic.inner +(@optic _.b) ∘ (@optic _.a) + +julia> optic.outer +(@optic _.a) + +julia> normalise(optic).inner +(@optic _.a) + +julia> normalise(optic).outer +(@optic _.c) ∘ (@optic _.b) +``` +""" +normalise(optic) = optic +normalise(optic::ComposedOptic) = foldl(∘, decompose(optic)) diff --git a/test/test_optics.jl b/test/test_optics.jl index adf774ea..db79a942 100644 --- a/test/test_optics.jl +++ b/test/test_optics.jl @@ -45,4 +45,18 @@ end @inferred modify(x -> 0, arr, @optic _ |> Elements() |> If(iseven)) end +@testset "Decomposition" begin + @test Accessors.decompose(@optic _.a.b.c) == ((@optic _.c), (@optic _.b), (@optic _.a)) +end + +@testset "Propname" begin + @test Accessors.propname(@optic _.a) == :a +end + +@testset "Normalisation" begin + @test Accessors.normalise((@optic _.c) ∘ (@optic _.a.b)).inner == @optic _.a + @test Accessors.normalise((@optic _.c) ∘ (@optic _.a.b)).outer.inner == @optic _.b + @test Accessors.normalise((@optic _.c) ∘ (@optic _.a.b)).outer.outer == @optic _.c +end + end#module