I would like to provide destructuring assignment as a syntactic sugar for a custom type.
I understand that the way to do it is iteration, but I am wondering if there is something more elegant than the MWE below, also compiler-friendly (constant folding of iterator state).
struct Foo{T}
a::T
b::T
c::T
end
function Base.iterate(foo::Foo, index = 1)
if index == 1
foo.a, 2
elseif index == 2
# NOTE: deliberately reverse c, b
foo.c, 3
elseif index == 3
foo.b, 4
else
nothing
end
end
which works as
julia> x, y, z = Foo(1, 2, 3);
julia> (x, y, z)
(1, 3, 2)
That is to say, is there a convenient shortcut which would just tell destructuring to collect the fields (:a, :c, :b)?
Just to clarify, the user would still manually specify the result, just not define the rather inelegant Base.iterate above.
Named tuple:
(; a, b, c) = Foo(1,2,3)
Works also with single fields
(; b) = Foo(5,6,7)
That was not my question; I specifically want destructuring via iterables (as linked in the docs), not property-based destructuring.
you could also do something along the lines of iterate(f::Foo, i=1) = (i <= nfields(f)) ? (getfield(f, i), i+1) : nothing
Note how b and c are exchanged, that is deliberate in the MWE.
perhaps instead of getfield(f, i) you can define some const permutation and make that getfield(f, PERM[i]) ?
or: iterate(f::Foo, state...) = iterate((f.a, f.c, f.b), state...)
This is a bit wacky, as it hides the result in the state:
# this can be reused, eg from a package
function proxy_iterate((proxy, inner_state...),)
result = iterate(proxy, inner_state...)
if result ≡ nothing
nothing
else
elt, inner_state′ = result
elt, (proxy, inner_state′)
end
end
# the user would define this
Base.iterate(foo::Foo, state = ((foo.a, foo.c, foo.b), )) = proxy_iterate(state)
You could convert your type to Tuple and use Tuple iteration:
Base.Tuple(foo::Foo) = (foo.a, foo.c, foo.b)
Base.iterate(foo::Foo, state...) = iterate(Tuple(foo), state...)