Closed tkf closed 5 years ago
Thanks for the PR. This is a great idea and we should probably have this here! I wonder if there is a mechanism to hook into e.g. @set, @lens, ...
macro and so rules like this could live in an external package. E.g. Kaleido.
I am a bit in a hurry, I will add more comments later.
It reminds me of Common Lisp's setf
, which also supported assigning to arbitrary expressions.
https://www.cs.cmu.edu/Groups/AI/html/cltl/clm/node80.html http://www.lispworks.com/documentation/HyperSpec/Body/m_defset.htm#defsetf
Some more comments:
We have to decide which package is responsible for defining all the set
methods. I think it makes sense to handle the Base
/stdlib
methods here. Stuff like DiffEqBase.isinplace
should be handled elsewhere.
I agree with all you say about handling callable structs. Should be possible in future, should not break API therfore document set(obj::T, ::typeof(@lens myfunction(_)), x)
should be the official way to overload. Might also make sense to actually print this type as typeof(@lens myfunction(_))
?
This is PR will break some code, but I think its worth it:
julia> using Setfield
julia> @set identity(2) = 3 3
julia> @macroexpand @set identity(2) = 3 quote
#3#lens = Setfield.compose()
#= /home/jan/.julia/packages/Setfield/gMRkV/src/sugar.jl:113 =#
#4###_#372 = Setfield.set(identity(2), #3#lens, begin
#= REPL[3]:1 =#
3
end)
end
@jw3126
- Stuff like
DiffEqBase.isinplace
should be handled elsewhere.
Totally agree. I mentioned DiffEqBase.isinplace
just to emphasise that I may not be using many "demo" lenses I added.
(But I just realized that @lens last(_)
is useful until #15 is fully resolved.)
- Might also make sense to actually print this type as
typeof(@lens myfunction(_))
?
We can. But we need to be extremely careful in the implementation: https://github.com/JuliaLang/julia/issues/29428
- This is PR will break some code
Ah, good point. I also should test that code like @set x[length(x) - 1] = 0
still works. Related to this, maybe we should ban calls with arity > 1 outside []
?
I wonder if there is a mechanism to hook into e.g.
@set, @lens, ...
macro and so rules like this could live in an external package. E.g. Kaleido.
I guess we need to build some kind of hook system to do that especially for parsing part of @lens
and @set
. But I think that'll be way more complicated than just adding @lens f(_)
-support directly. This is one of the reason why I opened a PR rather than experimenting in Kaleido.
@cstjean Interesting observation! So Setfield will be yet another proof that Julia is the LISP for the 21st century :)
There are probably tons of corner cases like
julia> first(42) 42
Ah... Of course. Or maybe there should be Base.setindex(::Number, v, i)
in Julia Base :laughing: . It would be an "interesting" exercise to try PRing it.
- Might also make sense to actually print this type as
typeof(@lens myfunction(_))
?We can. But we need to be extremely careful in the implementation: JuliaLang/julia#29428
Oh wow I somehow never ran into that bug. Yeah I think it's not worth to touch the printing of that type.
Can you document how to do the set
overloading?
This looks good to me! Shall we merge it?
Yes, I implemented what I wanted to add. Please go ahead and merge. Thanks for reviewing this!
Thanks for the PR. I like your idea a lot!
Feel free to tag a release if you need one!
I propose to add a new lens
FunctionLens(f)
with a sugar@lens f(_)
. By default, it only has theget
method which is defined to be:Unlike other lenses,
FunctionLens
needsset
method to be defined manually.It would be useful for manipulating objects that do not have clear indexing or property access. For example, manipulating parameterized types:
A scary part of this proposal is that Base/stdlib methods on Base/stdlib types would be accumulated in this package to avoid type piracy. It could bloat otherwise a slick package. My guess/hope is that there are not too many functions with useful
FunctionLens
definition as otherwise Base/stdlib wouldn't have been usable.I think the upside of having
@lens f(_)
is greater than the potential downside:Some discussion points:
FunctionLens(f)
definitions I added in this PR? They are more like demos than driven by my need. (An example of what I want is theFunctionLens
forDiffEqBase.isinplace
trait function.) Maybe it's better to define noset
methods and wait for someone needing it? Alternatively we can say that those definitions are provided on a provisional basis and we need someone to open a PR/issue to stabilize the API.struct FunctionLens{F}; f::F; end
instead of currentstruct FunctionLens{f}; end
but we need a bit of trick to support type constructor in a dispatchable manner (as otherwiseF
would beDataType
).FunctionLens
and say that you need to defineset(obj::T, ::typeof(@lens myfunction(_)), x)
(i.e., rather than saying that you can useset(obj::T, ::FunctionLens{myfunction}, x)
).typeof(@lens myfunction(_))
is a good way to go for now and supporting only functions is OK until someone needs non-functions.typeof(@lens myfunction(_))
would be forward compatible.