# ANN: Setfield.jl Yet another package for working with immutables

**URL:** <https://discourse.julialang.org/t/ann-setfield-jl-yet-another-package-for-working-with-immutables/9267>\
**Category:** General Usage\
**Tags:** package, announcement\
**Created:** [February 22, 2018, 10:26pm UTC](https://discourse.julialang.org/t/ann-setfield-jl-yet-another-package-for-working-with-immutables/9267 "2018-02-22T22:26:46Z")\
**Posts on this page:** 20\
**Page:** 1

<div class="post-metadata">

**Author:** ![jw3126](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/jw3126/32/3086_2.png) [@jw3126](https://discourse.julialang.org/u/jw3126)\
**Post date:** [February 22, 2018, 10:26pm UTC](https://discourse.julialang.org/t/ann-setfield-jl-yet-another-package-for-working-with-immutables/9267/1 "2018-02-22T22:26:46Z")

</div>

[Setfield.jl](https://github.com/jw3126/Setfield.jl) is a package for modifying immutables. It allows to write code like

```julia
@set obj.a.b.c = foo

```

which yields a duplicate of `obj` with the appropriate field replaced. Compared to [Reconstructables.jl](https://discourse.julialang.org/t/ann-new-package-reconstructables-jl-for-easier-modifications-of-immutable-structs/9157) it has the following advantages:

- Fully typestable, therefore good performance
- Does not require keyword constructors
- Supports indexing, e.g. `@set obj.my_tuple[5] = 123`
- Supports updates like `@set obj.velocity[2] += 2`

---

<div class="post-metadata">

**Author:** ![tkf](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/tkf/32/17635_2.png) [@tkf](https://discourse.julialang.org/u/tkf)\
**Post date:** [February 23, 2018, 12:30am UTC](https://discourse.julialang.org/t/ann-setfield-jl-yet-another-package-for-working-with-immutables/9267/2 "2018-02-23T00:30:25Z")

</div>

Julia implementation of Lens using generated function! This is super cool 🙂

Though I suppose the type stability is not coming from lens but rather because you are using the default constructor (or the order of arguments to the constructor has to match with that of field).

I wonder if it makes sene for Julia lang itself to have lens and build “immutable struct mutation” on top of it:  
[https://github.com/JuliaLang/julia/pull/21912](https://github.com/JuliaLang/julia/pull/21912)

---

<div class="post-metadata">

**Author:** ![JeffreySarnoff](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/jeffreysarnoff/32/1980_2.png) [@JeffreySarnoff](https://discourse.julialang.org/u/JeffreySarnoff)\
**Post date:** [February 23, 2018, 3:56am UTC](https://discourse.julialang.org/t/ann-setfield-jl-yet-another-package-for-working-with-immutables/9267/3 "2018-02-23T03:56:36Z")

</div>

what happens when the struct has one (or two) primitive fields and ten of these are allocated and stuffed next to each other in sequence forming an immediate, not indirect manner of indexing and the first field of the sixth struct has its value changed?

---

<div class="post-metadata">

**Author:** ![jw3126](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/jw3126/32/3086_2.png) [@jw3126](https://discourse.julialang.org/u/jw3126)\
**Post date:** [February 23, 2018, 9:30am UTC](https://discourse.julialang.org/t/ann-setfield-jl-yet-another-package-for-working-with-immutables/9267/4 "2018-02-23T09:30:27Z")

</div>

I think there is no way to get full type stability here without generated functions at some point.  
In order to manipulate fields, you need to process `fieldnames(typeof(obj))` in one way or another. Without generated functions, this looks a priori like an arbitrary vector of symbols to the compiler. The compiler would need to

- Figure out that its safe to “evaluate” this vector at compile time
- Fully unroll the loop/recursion you use to process the fields
- Constant propagate all the field symbols

I am not a compiler guy, but to me this seems incredibly hard.

---

<div class="post-metadata">

**Author:** ![jw3126](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/jw3126/32/3086_2.png) [@jw3126](https://discourse.julialang.org/u/jw3126)\
**Post date:** [February 23, 2018, 9:35am UTC](https://discourse.julialang.org/t/ann-setfield-jl-yet-another-package-for-working-with-immutables/9267/5 "2018-02-23T09:35:51Z")

</div>

Not sure I understand the question, can you provide a code example?

---

<div class="post-metadata">

**Author:** ![tkf](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/tkf/32/17635_2.png) [@tkf](https://discourse.julialang.org/u/tkf)\
**Post date:** [February 23, 2018, 9:59am UTC](https://discourse.julialang.org/t/ann-setfield-jl-yet-another-package-for-working-with-immutables/9267/6 "2018-02-23T09:59:52Z")

</div>

I see. Thanks. That makes sense.

I think this package would be perfect if it supports custom inner constructors with type parameters. Any plan to support constructor like `B{T}` (as in [https://github.com/tkf/Reconstructables.jl#how-to-use-type-parameters](https://github.com/tkf/Reconstructables.jl#how-to-use-type-parameters))?

---

<div class="post-metadata">

**Author:** ![jw3126](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/jw3126/32/3086_2.png) [@jw3126](https://discourse.julialang.org/u/jw3126)\
**Post date:** [February 23, 2018, 10:28am UTC](https://discourse.julialang.org/t/ann-setfield-jl-yet-another-package-for-working-with-immutables/9267/7 "2018-02-23T10:28:05Z")

</div>

Why not make the inner constructor an outer constructor?

```julia
julia> struct B{T, X, Y}
           x::X
           y::Y
       end

julia> B{T}(x::X, y::Y = 2) where {T, X, Y} = B{T, X, Y}(x, y)

julia> b = B{1}(2,3)
B{1,Int64,Int64}(2, 3)

julia> using Setfield

julia> @set b.x = 512
B{1,Int64,Int64}(512, 3)

```

---

<div class="post-metadata">

**Author:** ![tkf](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/tkf/32/17635_2.png) [@tkf](https://discourse.julialang.org/u/tkf)\
**Post date:** [February 23, 2018, 10:29am UTC](https://discourse.julialang.org/t/ann-setfield-jl-yet-another-package-for-working-with-immutables/9267/8 "2018-02-23T10:29:52Z")

</div>

Can you “change” the type of b by `b_new = @set b.x = 1.0`?

---

<div class="post-metadata">

**Author:** ![jw3126](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/jw3126/32/3086_2.png) [@jw3126](https://discourse.julialang.org/u/jw3126)\
**Post date:** [February 23, 2018, 10:30am UTC](https://discourse.julialang.org/t/ann-setfield-jl-yet-another-package-for-working-with-immutables/9267/9 "2018-02-23T10:30:32Z")

</div>

No you can’t do that. Note that you can’t do that with mutable structs either.

---

<div class="post-metadata">

**Author:** ![jw3126](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/jw3126/32/3086_2.png) [@jw3126](https://discourse.julialang.org/u/jw3126)\
**Post date:** [February 23, 2018, 10:34am UTC](https://discourse.julialang.org/t/ann-setfield-jl-yet-another-package-for-working-with-immutables/9267/10 "2018-02-23T10:34:31Z")

</div>

To me changing the type like this seems strange. Can you give an example, where that would be useful?

---

<div class="post-metadata">

**Author:** ![tkf](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/tkf/32/17635_2.png) [@tkf](https://discourse.julialang.org/u/tkf)\
**Post date:** [February 23, 2018, 10:47am UTC](https://discourse.julialang.org/t/ann-setfield-jl-yet-another-package-for-working-with-immutables/9267/11 "2018-02-23T10:47:15Z")

</div>

A good example that you might want to change is the whole family of problem struct in DifferentialEquations.jl:  
[http://docs.juliadiffeq.org/latest/types/ode\_types.html](http://docs.juliadiffeq.org/latest/types/ode_types.html)

You may want to change the initial condition `u0` type from Vector to Matrix, or regular array to static array or GPU array, etc. This is going to change the type parameters of the problem type.

---

<div class="post-metadata">

**Author:** ![jw3126](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/jw3126/32/3086_2.png) [@jw3126](https://discourse.julialang.org/u/jw3126)\
**Post date:** [February 23, 2018, 11:05am UTC](https://discourse.julialang.org/t/ann-setfield-jl-yet-another-package-for-working-with-immutables/9267/12 "2018-02-23T11:05:24Z")

</div>

Okay I think we can do that. We need ways to hook into `@set`. I think

```julia
@set obj.x = 1.0

```

should call `setproperty(b, Val{:x},1.0)` and then the user could overload `setproperty` to get any kind of interesting behaviour. The default implementation of `setproperty` should be something like

1. destructure `obj` into properties
2. manipulate properties
3. construct `new_obj` from `typeof(obj)` and properties

Each step should be overloadable. 3) is where you could hook in your

```julia
Reconstructables.constructor_of(::Type{<: B{T}}) where T = B{T}

```

What do you think?

---

<div class="post-metadata">

**Author:** ![tkf](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/tkf/32/17635_2.png) [@tkf](https://discourse.julialang.org/u/tkf)\
**Post date:** [February 23, 2018, 11:51am UTC](https://discourse.julialang.org/t/ann-setfield-jl-yet-another-package-for-working-with-immutables/9267/13 "2018-02-23T11:51:08Z")

</div>

Hmm… In that case, do you need to call `setproperty` from `set(l::FieldLens{field}, ...)` which is a generated function? If so, my understanding is that it is not going to work since the codegen part of the generated function has to be pure. Allowing users to “register” their definition in `setproperty` is not pure.

I actually had a similar trouble with `constructor_of` and generated function. [https://github.com/tkf/Reconstructables.jl/pull/1](https://github.com/tkf/Reconstructables.jl/pull/1)

---

<div class="post-metadata">

**Author:** ![jw3126](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/jw3126/32/3086_2.png) [@jw3126](https://discourse.julialang.org/u/jw3126)\
**Post date:** [February 23, 2018, 12:05pm UTC](https://discourse.julialang.org/t/ann-setfield-jl-yet-another-package-for-working-with-immutables/9267/14 "2018-02-23T12:05:58Z")

</div>

I think it can be done roughly like

```julia
function set(l::Fieldlens, obj, val)
    setproperty(obj, field(l), val)
end
function setproperty(obj, field, val) # field is say a Val
     fields = replace_field(obj, field, val) # fields is a tuple
     constructor_of(obj)(fields...)
end
@generated replace_field...

```

---

<div class="post-metadata">

**Author:** ![mauro3](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/mauro3/32/292_2.png) [@mauro3](https://discourse.julialang.org/u/mauro3)\
**Post date:** [February 23, 2018, 1:56pm UTC](https://discourse.julialang.org/t/ann-setfield-jl-yet-another-package-for-working-with-immutables/9267/15 "2018-02-23T13:56:08Z")

</div>

Cool, this looks like it could/should replace the `reconstruct` function in [Parameters.jl](https://github.com/mauro3/Parameters.jl).

Are generated functions needed? At least for the getter probably not. Consider (Julia 0.6):

```julia
julia> struct A; a::Int end                                                                                                                                              
                                                                                                                                                                         
julia> f(::Val{S}, obj) where S = getfield(obj, S)
f (generic function with 1 method)

julia> @code_warntype f(Val(:a), A(5))
Variables:
  #self# <optimized out>
  #unused# <optimized out>
  obj::A

Body:
  begin                                                                                                                                                                  
      return (Main.getfield)(obj::A, $(Expr(:static_parameter, 1)))::Int64                                                                                               
  end::Int64                                                                                                                                                             

```

(Note that in Julia 0.7 the `Val` trick is not needed anymore.)

I think the getter can be re-written in above style ([https://github.com/jw3126/Setfield.jl/blob/4dcf12784d3ca1c800e3d9fe2b7c844b6029d2d0/src/lens.jl#L96](https://github.com/jw3126/Setfield.jl/blob/4dcf12784d3ca1c800e3d9fe2b7c844b6029d2d0/src/lens.jl#L96)).

For the setter, generated functions are probably needed, although in Julia 0.7 maybe they could be avoided by recursions. But I’m not sure whether that would be of any benefit.

---

<div class="post-metadata">

**Author:** ![jw3126](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/jw3126/32/3086_2.png) [@jw3126](https://discourse.julialang.org/u/jw3126)\
**Post date:** [February 23, 2018, 2:03pm UTC](https://discourse.julialang.org/t/ann-setfield-jl-yet-another-package-for-working-with-immutables/9267/16 "2018-02-23T14:03:41Z")

</div>

Cool thanks!

---

<div class="post-metadata">

**Author:** ![ChrisRackauckas](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/chrisrackauckas/32/77_2.png) [@ChrisRackauckas](https://discourse.julialang.org/u/ChrisRackauckas)\
**Post date:** [February 23, 2018, 2:10pm UTC](https://discourse.julialang.org/t/ann-setfield-jl-yet-another-package-for-working-with-immutables/9267/17 "2018-02-23T14:10:01Z")

</div>

It should be noted that keyword arguments are only an issue on v0.6, so there’s no reason to make such a big deal about that. Generated functions are not statically compilable, so that would be a big deal down the road.

---

<div class="post-metadata">

**Author:** ![mauro3](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/mauro3/32/292_2.png) [@mauro3](https://discourse.julialang.org/u/mauro3)\
**Post date:** [February 23, 2018, 2:17pm UTC](https://discourse.julialang.org/t/ann-setfield-jl-yet-another-package-for-working-with-immutables/9267/18 "2018-02-23T14:17:08Z")

</div>

But it would be nice to use this pattern also without having keyword constructors. At least for as long as keyword constructors are not defined automatically.

---

<div class="post-metadata">

**Author:** ![jw3126](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/jw3126/32/3086_2.png) [@jw3126](https://discourse.julialang.org/u/jw3126)\
**Post date:** [February 23, 2018, 3:44pm UTC](https://discourse.julialang.org/t/ann-setfield-jl-yet-another-package-for-working-with-immutables/9267/19 "2018-02-23T15:44:55Z")

</div>

I guess in the long run the problem will be solved in [Base](https://github.com/JuliaLang/julia/pull/21912) anyway. Keyword constructors are nice for large structs. However they are only an option, if you own the struct. Adding one to foreign structs is annoying and leads to bugs. Also not every `Point(x,y)` should have its own keyword constructor.

---

<div class="post-metadata">

**Author:** ![tkf](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/tkf/32/17635_2.png) [@tkf](https://discourse.julialang.org/u/tkf)\
**Post date:** [February 23, 2018, 8:42pm UTC](https://discourse.julialang.org/t/ann-setfield-jl-yet-another-package-for-working-with-immutables/9267/20 "2018-02-23T20:42:58Z")

</div>

Oops. Forget what I said 🙂 You can call `setproperty` inside the code generated by a generated function. You wouldn’t call it inside the codegen code part so it’s not a problem.

Yeah, the code looks good to me.

[Next page](https://discourse.julialang.org/t/ann-setfield-jl-yet-another-package-for-working-with-immutables/9267.md?page=2)
