Skip to content
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

Multi argument modify? #137

Open
jw3126 opened this issue May 15, 2020 · 2 comments
Open

Multi argument modify? #137

jw3126 opened this issue May 15, 2020 · 2 comments

Comments

@jw3126
Copy link
Owner

jw3126 commented May 15, 2020

From time to time, I do an add hoc implementation of the following functionality:

using Setfield
import Setfield: modify

"""
    modify(f, obj, obj1, obj2..., lens)

Multi argument `modify`. Apply `set` to `obj` with the value obtained from `f` on the `get` of all objects.

"""
function modify(f, objects_lens...)
    lens = last(objects_lens)
    args = let objects = Base.front(objects_lens),
        lens=lens
        map(objects) do o
            get(o, lens)
        end
    end
    val = f(args...)
    o1 = first(objects_lens)
    return set(o1, lens, val)
end

Usage is as follows:

meas1 = (unit=:cm, values=[1,2])
meas2 = (unit=:cm, values=[3,4])
modify(+, meas1, meas2, @lens(_.values))
(unit = :cm, values = [4, 6])

It has footgun potential though:

meas1 = (unit=:cm, values=[1,2])
meas2 = (unit=:mm, values=[3,4])
modify(+, meas1, meas2, @lens(_.values))
(unit = :cm, values = [4, 6])
@tkf
Copy link
Collaborator

tkf commented May 15, 2020

Oh, it looks useful. It's a bit like mergewith? By the way, why not val = foldl(f, args) instead of val = f(args...)? (Then it's pretty close to mergewith.)

Maybe all the field names and values except the one specified by the lens have to match? Throwing when it's not the case seems to be useful to avoid the footgun. Though I guess it's not implementable only by using lens. I guess you can do this by setting the value from the first object to other objects and then make sure everything is equal.

Regarding the method signature, for this function, I think having lens as the second argument makes sense (even though I argued for lens to be the last for modify). It'd be nice to have something like $nice_name((lens1 => f1, lens2 => f2, ...), obj1, obj2, ...) to do some kind of batched merge.

@tkf
Copy link
Collaborator

tkf commented Jul 11, 2020

FYI, I created something similar called modifying and put it DataTools.jl:

julia> using DataTools

julia> map(modifying(a = string), [(a = 1, b = 2), (a = 3, b = 4)])
2-element Array{NamedTuple{(:a, :b),Tuple{String,Int64}},1}:
 (a = "1", b = 2)
 (a = "3", b = 4)

julia> reduce(modifying(a = +), [(a = 1, b = 2), (a = 3, b = 4)])
(a = 4, b = 2)

julia> using Setfield

julia> map(modifying(@lens(_.a[1].b) => x -> 10x),
           [(a = ((b = 1,), 2),), (a = ((b = 3,), 4),)])
2-element Array{NamedTuple{(:a,),Tuple{Tuple{NamedTuple{(:b,),Tuple{Int64}},Int64}}},1}:
 (a = ((b = 10,), 2),)
 (a = ((b = 30,), 4),)

--- https://juliafolds.github.io/DataTools.jl/dev/#DataTools.modifying

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants