I would like pack/unpack (I am using the excellent Parameters package macros for this) specific fields of a struct within a function. I created example code here:
using Parameters
using Distributions
#construct example struct
mutable struct Model{E<:VariateForm, F<:VariateForm}
α :: Vector{<:Distribution{E}} # Transition
β :: Vector{<:Distribution{F}} # Observation
end
#test
model = Model( [Categorical([.5,.5]), Categorical([.5,.5])], [Normal(), Normal()])
info = fieldnames(Model)
function update!(model::Model, info) #Not Working
################# Unpack all model parameter that are in info field
#Not Working
for i in eachindex(info)
@unpack @eval $(info[i]) = model
end
################# #Do something - just exemplary here to get different output
#Working
@eval $(info[1]) = [Normal(1,2), Gamma(1,2)]
@eval $(info[2]) = [Poisson(1), Normal(3,4)]
################# Pack all model parameter that are in info field
#Not Working
for i in eachindex(info)
@pack! model = @eval $(info[i])
end
end
Question 1: I would like to unpack all the fields that come from a specific tuple but calling both @unpack and @eval simultaneously does not seem to work, even though calling both individually works. Does anyone know a solution if I would be forced to call 2 macros simultaneously?
Question 2: On a related note, does anyone have a method to just unpack all parameter of a general struct?
Thank you for your answer! I saw in the docs that one may use the @unpack_* for @with_kw structs, I was wondering if it might also be possible for general containers.
I read that @eval will evaluate in the global scope, but so far it was the only way for me to call and assign values to symbols like this:
@eval $(info[1]) = [Normal(1,2), Gamma(1,2)]
I dont really understand why dicts would be better in this case as I would also only collect the symbols from there. The Model struct is quite a bit bigger in the real code and I perform many operations on it. I can always dispatch update! on different subtypes of it, but if more Models become available I ideally would have a more general update! function where I dont need to more and more code to it but instead generically updates based on some information.
No, this cannot be achieved with a macro as this only has information on the syntax. When a macro sees a, it only sees :a and not its contents (the macro is evaluated before a gets its contents). Thus it could not know what fields a has to unpack.
Using evals, in particular in local scopes such as functions, is considered bad style. Further it makes Julia slow as it cannot reason about types anymore (see performance section, about global variables applies here as well). Another reason is that you can get name-clashes, say your info contains a field which is identical to a global variable of yours, bam:
module A
cabbage(x) = 2 # so cabbage is a global
function f(inp)
@eval $inp = 7
return nothing
end
end
using .A
A.f(:lettuce) # ok
A.f(:cabbage) # error: ERROR: invalid redefinition of constant cabbage
So, your info structs interact in a non-trivial way with the rest of your code, changing either can lead to breakage.
So, afaik, instead of using macros I could build a Dict with they info keys and some values, and then loop through a mutable struct to update information, and to avoid ambiguity?