I think there's a much more idiomatic approach that doesn't require macros or any other packages. The documentation suggests the most idiomatic approach, for instance the docs for Matrix factorization.
In particular, this line:
julia> l, u, p = lu(A); # destructuring via iteration
which suggests iteration is the right way to go. So all that is left is to make your struct implement the iteration interface
A really simple but un-idiomatic example:
Your struct looks like this
struct MyModel
a::Int = 5
b::Float64 = 5.5
c::Matrix{Float64} = rand(3,4)
# 40 other parameters go here
end
To implement the iteration interface you need to define Base.iterate
which takes MyModel
as a parameter and a "state". That function returns the element that corresponds to the state, and calls the next iteration (sort of like a linked list). For example:
function Base.iterate(m::MyModel, state)
if state == 1
return(m.a, state+1)
elseif state == 2
return(m.b, state+1
elseif state == 3
return(m.c, state+1)
else
return nothing
end
end
In julia an iteration stops when next(iter) == nothing
, that's why you have to return nothing
when there is nothing left to iterate over.
Advanced example
You can find a much more idiomatic (but contrived) example in the source code for the lu
factorization:
# iteration for destructuring into components
Base.iterate(S::LU) = (S.L, Val(:U))
Base.iterate(S::LU, ::Val{:U}) = (S.U, Val(:p))
Base.iterate(S::LU, ::Val{:p}) = (S.p, Val(:done))
Base.iterate(S::LU, ::Val{:done}) = nothing
which uses Val
for some compile-time optimizations if I'm not mistaken.
In the particular case of a struct with 40 fields I can't think of a more ergonomic way, maybe the data is better suited for another storage option.