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

Support insert with dynamic DynamicIndexLens #132

Closed
wants to merge 4 commits into from

Conversation

MasonProtter
Copy link
Contributor

Before:

julia> let v = [1,2,3]
           @insert v[end+1] = 4
       end
ERROR: MethodError: no method matching insert(::Vector{Int64}, ::Accessors.DynamicIndexLens{var"#6#7"}, ::Int64)

after:

julia> let v = [1,2,3]
           @insert v[end+1] = 4
       end
4-element Vector{Int64}:
 1
 2
 3
 4

Copy link
Member

@jw3126 jw3126 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM thanks a lot @MasonProtter. Actually surprised that this did not came up before.

@MasonProtter
Copy link
Contributor Author

Happy to help!

@aplavin
Copy link
Member

aplavin commented Feb 7, 2024

I'd recommend being very cautious with adding something like this. Typically, we have f(insert(obj, f, val)) == val, and here this property doesn't (and cannot) hold. That's the reason I didn't add such a method when adding insert.

Try @insert last(obj) = val if you want to "push" a new element. This already works and doesn't break that nice property.

@MasonProtter
Copy link
Contributor Author

What is nice about that property?

@aplavin
Copy link
Member

aplavin commented Feb 7, 2024

These optics laws are quite deeply ingrained into Accessors, and make it possible to reason about optics code generically.

The most basic one is f(set(obj, f, val)) == val. It's even codified in tests at https://github.com/JuliaObjects/Accessors.jl/blob/master/ext/AccessorsTestExt.jl#L12-L13.
The insert function is most naturally thought of as "like set, but when the target doesn't exist yet". This is also how the situation is set up in optics libraries in other languages.
Without this property, it become challenging to even define what insert does.

So, unless there's a very strong reason to break this law, would be best to keep following it.

@MasonProtter
Copy link
Contributor Author

MasonProtter commented Feb 7, 2024

I guess I'd say this sort of use-case this seems to be the entire point of DynamicIndexLens, but I'm nowhere near as familiar with the codebase or what could go wrong if that law isn't followed, so I'll let you guys decide if this should exist or not.

@MasonProtter
Copy link
Contributor Author

@jw3126 any thoughts on @aplavin's point?

@aplavin
Copy link
Member

aplavin commented Feb 27, 2024

Just wanted to clarify the abstract law reasoning with a concrete example.
Don't you find it confusing and error-prone that @insert x[begin] = 1 would insert 1 as the first element, but @insert x[end] = 1 would insert as the second-to-last element? Compare with first/last symmetry.

A neat way to make @insert last(x) = 1 more discoverable is to add a register_error_hint for MethodError with DynamicIndexLens. That would actually be great!
This is getting more and more use in Julia, eg

julia> max([1,2])
ERROR: MethodError: no method matching max(::Vector{Int64})
The function `max` exists, but no method is defined for this combination of argument types.
Finding the maximum of an iterable is performed with `maximum`.

@jw3126
Copy link
Member

jw3126 commented Feb 27, 2024

Yeah I think promoting last is the better option here.

@MasonProtter
Copy link
Contributor Author

Sounds good.

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

Successfully merging this pull request may close these issues.

3 participants