I’m trying to use the setproperties function of Setfield.jl to set multiple different fields at once for immutable structs, but I’m running into this issue.
using Setfield
struct A
foo::Int
bar::Int
end
struct B
baz::A
end
y = A(1, 2)
x = B(A(1, 2))
y = setproperties(y, (foo = 2, bar = 1))
x = setproperties(x, (baz.foo = 2, baz.bar = 1))
The second to last line works, but the last line has this error
syntax: invalid named tuple field name “baz.foo”
Stacktrace:
[1] top-level scope at …/.julia/packages/IJulia/DrVMH/src/kernel.jl:52
I know I can use @set to do this, but I’m trying to limit the number of times the struct is copied. Is there any way to do this using Setfield.jl or a similar tool?
Looking at help?> setproperties
using it in the nested case is not supported, which makes sense to me.
The patch::NamedTuple argument is meant to be using parts of the properties of the struct.
So it should be e.g.
x = setproperties(x, (baz = setproperties(y, (foo = 2, bar = 1)) ))
if you like this (I don’t). Or x = setproperties(x, (baz = A(2,1) ))
By the way, your examples do not need setproperties. They are just the same as setting a new y overwritting the old one:
y = A(2,1)
y = A(1,2)
It would make more sense if these are the examples:
y = A(1, 2)
y = setproperties(y, (foo = 2))
and
x = B(A(1, 2))
x = setproperties(x, (baz = setproperties(y, (foo = 2)) ))
now using setproperties here is a good MWE for larger structs with many properties.
setproperties does not reduce the number of copies. It is a convenience function for large structs where only small number of properties are changing when copied.
using Setfield
struct A
foo::Int
bar::Int
end
struct B
baz::A
end
x = B(A(1, 2))
function with_set_properties(x)
return setproperties(x, (baz = setproperties(x.baz, foo = 2, bar = 1)))
end
function without_set_properties(x)
return @set (@set x.baz.foo = 2).baz.bar = 1
end
@time y = with_set_properties(x)
@time z = without_set_properties(x);
It seems the former is more performant
0.006966 seconds (19.32 k allocations: 1.048 MiB)
0.010215 seconds (29.68 k allocations: 1.623 MiB)
and does fewer allocations (which I suspect is where the performance comes from).
Yeah, my real use case for this is much more complicated, I was just trying to make as simple an example as possible that involved changing multiple, nested fields
But the difference is so small that it is perhaps just an artefact.
You may search the discussions for allocations and benchmarking. I remember there are some good explanations about this, which may help in understanding whats going on.