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

SettableRef object #56

Open
cstjean opened this issue Nov 9, 2018 · 3 comments
Open

SettableRef object #56

cstjean opened this issue Nov 9, 2018 · 3 comments

Comments

@cstjean
Copy link

cstjean commented Nov 9, 2018

It occurred to me this morning that with the new setproperty!/getproperty methods, it might be possible to do something like:

o = SettableRef(some_immutable_object)
o.f.x = 10      
o.f.y = 23
some_immutable_object = get(o)   # unwrap the object

as syntax equivalent to

@set some_immutable_object.f.x = 10
@set some_immutable_object.f.y = 23

Internally, o.obj would contain the immutable, and o.f (getproperty(o, :f)) would return a proxy object p so that p.x = 10 (setproperty(p, :x, 10)) would be equivalent to o.obj = @set o.f.x = 10.

It's not a great win over @set, which is already pretty concise. But I think for us it would be interesting, as it turns an immutable object into (what looks like) a regular mutable object, without any scary macro involved. Was that discussed anywhere?

@jw3126
Copy link
Owner

jw3126 commented Nov 9, 2018

It is an interesting idea. AFAICT it was not discussed before. Some things that are challenging with this approach are:

  • Change the type of the object
  • Performance, at least with the current julia compiler this will allocate.

@tkf
Copy link
Collaborator

tkf commented Jul 31, 2019

Here is a super simple implementation without nested assignment support:

using Setfield: set, PropertyLens

mutable struct Mutable{T}
    value::T
end

thaw(imut) = Mutable(imut)
freeze(mut::Mutable) = getfield(mut, :value)

Base.setproperty!(mut::Mutable, name::Symbol, value) =
    setfield!(mut, :value, set(freeze(mut), PropertyLens{name}(), value))

Base.getproperty(mut::Mutable, name) = getproperty(freeze(mut), name)

It looks like the compiler can eliminate the allocation in a simple case like this:

f() = f((a=1,)).a

function f(x)
    y = thaw(x)
    y.a = 2
    return freeze(y)
end

@code_typed optimize=true f()

prints

CodeInfo(
1return 2
) => Int64
  • Change the type of the object

Maybe not being able to change the type is a feature in some sense? The user is signaling the compiler that the type never changes in this API. So it may be possible that the compiler has easier time inferring the code? This is just a speculation, though.

@tkf
Copy link
Collaborator

tkf commented Jul 31, 2019

(I used thaw/freeze (inspired by JuliaLang/julia#31630) instead of Ref-like API because it also makes sense to wrap Tuple/NamedTuple/StaticArray/etc. in this API.)

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

3 participants