-
Notifications
You must be signed in to change notification settings - Fork 19
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Differences to Setfield.jl? #9
Comments
There is an incomplete list of changes here. Some goals were to make lenses more light weight, in particular duck typed and avoid overloading Can you given an example what kind of |
My main practical use case now is https://github.com/rafaqz/ModelParameters.jl The main thing missing from Accessors.jl that you can do with Flatten.jl is context about the leaves on the tree that are replaced. What is their parent object, what is the field name, how to call a function of those two things (with fieldname in |
This is most of Flatten.jl I rewrote a while ago for Setfield.jl lenses, with It does pretty much everything I need. It composes with lenses and mostly has no runtime cost. It could be a separate package ObjectQueries.jl or it could go in Accessors.jl. I'm just not sure how to combine it with using Setfield
using Setfield: ComposedLens
struct SkipNone end
abstract type Query{S,R} <: Lens end
struct Select{S,R} <: Query{S,R} end
Select{S}() where {S} = Select{S,SkipNone}()
struct Context{X,S,R} <: Query{S,R} end
Context{X,S}() where {X,S} = Context{X,S,SkipNone}()
struct FieldName end
struct ParentObj end
struct ParentType end
@inline query(lens::Select, obj, ::Val{FN}, val) where FN = (val,)
@inline query(lens::ComposedLens, obj, fn::Val{FN}, val) where FN =
(get(query(lens.outer, obj, fn, val)[1], lens.inner),)
@inline query(lens::Context{FieldName}, obj, ::Val{FN}, val) where FN = (FN,)
@inline query(lens::Context{ParentObj}, obj, ::Val{FN}, val) where FN = (obj,)
@inline query(lens::Context{ParentType}, obj, ::Val{FN}, val) where FN = (typeof(obj),)
@inline Setfield.get(obj, lens::Union{ComposedLens{<:Query},Query}) = _get(obj, lens)
@inline Setfield.set(obj, lens::Union{ComposedLens{<:Query},Query}, x) = _set(obj, lens, x)
@generated function _get(obj::O, lens::Union{ComposedLens{<:Query{T,S}},Query{T,S}}
) where {O,T,S}
exp = Expr(:tuple)
for fn in fieldnames(O)
v = quote
fn = $(QuoteNode(fn))
val = getfield(obj, fn)
if val isa T
query(lens, obj, Val{fn}(), val)
elseif !(T isa S)
_get(val, lens)
else
()
end
end
# Splat the result into the output tuple
push!(exp.args, Expr(:..., v))
end
exp
end
using BenchmarkTools
context = Context{FieldName,Real}()
lens = Select{Float64}()
combined = Select{NamedTuple}() ∘ @lens _.b
julia> @btime Setfield.get((7, (a=17.0, b=2.0f0), ("3", 5)), $context)
@btime Setfield.get((7, (a=17.0, b=2.0f0), ("3", 5)), $lens)
2.090 ns (0 allocations: 0 bytes)
(1, :a, :b, 2)
julia> @btime Setfield.get((7, (a=17.0, b=2.0f0), ("3", 5)), $lens)
0.019 ns (0 allocations: 0 bytes)
(17.0,)
julia> @btime Setfield.get((7, (a=17.0, b=2.0f0), ("3", 5)), $combined)
0.019 ns (0 allocations: 0 bytes)
(2.0f0,) |
Nice! I really like that this compiles into fast code. Functionlity like this is in scope of optic = @optic _ |> Elements() |> PushCtx() |> _.a |> If(isodd)
obj = [(a=1, b=10), (a=2, b=20), (a=3,b=30)
getall(obj, optic) == [(1, (a=1, b=10)), (3, (a=3, b=30))]
modify(obj, optic) do a, ctx
a + ctx.b
end == [(a=11, b=10), (a=2, b=20), (a=33,b=30)] So you can specify in your query with |
That's cool! I couldn't think of how context would work and still compose with lenses and have the context pass through, or how Being really fast is nice for programming style, you can just use the lens result as if that's what the object actually is, without thinking about the lens being there. |
Basically yes, with the minor detail that I had
Yes, I hate if I have to think during programming 😄 |
Ok cool, I read this as getall(obj, optic) == [((a=1, b=10), 1), ((a=3, b=30),3)] Maybe I misunderstand what |
Ah right, I was inconsistent. In the |
Sry the modify example is also wrong, I will edit my post above. |
This package is great to see. Just wondering if you have a list of differences and changes from Setfield.jl - current or intended?
Edit: Looks like
Recursive
can do pretty flexible descent/tree walking wIthFilter
! That's a lot of what I need to replace Flatten.jl.The text was updated successfully, but these errors were encountered: