jw3126 / Setfield.jl

Update deeply nested immutable structs.
Other
165 stars 17 forks source link

Some helper functions #150

Open pevnak opened 3 years ago

pevnak commented 3 years ago

Dear All,

I am using setfield for indexing in terribly complicated tree structures naturally present in tree data. Since I frequently need programmatically adapt lenses, it helps a lot, if they are in normalized form. I have therefore made a few functions, which simplifies my life a lot and I wonder, if that would be something you would consider interesting.

The first function "normalizes" lenses, such that outerlens in CompoundLens is simple.

lenskeys(lens::Setfield.ComposedLens) = vcat(lenskeys(lens.outer), lenskeys(lens.inner))
lenskeys(lens::Setfield.PropertyLens{K}) where {K} = [K]

function normalizelens(lens::Setfield.ComposedLens)
  ls = lenskeys(lens)
  mapfoldr(k -> Setfield.PropertyLens{k}(), ∘, ls)
end

And when I have the lenskeys, I can easily implement indexing as

function Base.getindex(lens::Setfield.ComposedLens, i...)
  ls = lenskeys(lens)
  mapfoldr(k -> Setfield.PropertyLens{k}(), ∘, ls[i...])
end

Base.lastindex(lens::Setfield.ComposedLens) = length(lenskeys(lens))

I would like to know your opinion and if they would fit to the library. If so, I would prepare a full PR.

Thanks a lot, Tomas

jw3126 commented 3 years ago

Hi @pevnak glad you are using Setfield.jl. We can include some form of this functionality, but I have some objections to the exact proposal.

What I am happy to include is some helper like this:

using Accessors
import Accessors: ComposedOptic

decompose(optic) = (optic,)
decompose(optic::ComposedOptic) = (decompose(optic.outer)..., decompose(optic.inner)...)

A PR to Accessors.jl would be welcome!

You could then do the indexing like ∘(decompose(@optic(_.a.b.c))[[1,3]]...). Note that this is also much more general and not restricted to PropertyLens.

jw3126 commented 3 years ago

Actually I think the decompose function can even go to CompositionsBase.jl.

pevnak commented 3 years ago

I like the decompose a lot. It is nicer and cleaner over what I have written. The indexing is also very nice.

To which part you refer as not being type stable? The normalization was mean to ensure that the outer part of ComposedLens is simple, which I think is not against the type stability. In my ideal view, the output of a composition would be a normalized Lens, but that might not be compatible some people use cases.

I can create these PRs with Tests to Accessors.jl. What should be the part of it?

jw3126 commented 3 years ago
pevnak commented 3 years ago

This is crystal clear answer.

I will work on relevant PRs.