Initialization of mutable structs with many default values


#1

Hey,
just wondered if there is a way to overcome “duplication” in default field names in mutable structs? what do I mean. Assume I have a struct which holds 100 fields,

mutable struct MyStruct
param1::Int64
param2::Float64
param3::Any
.
.
.
params100

and I wish to initialize it with default values for param1-100.
So if I understood correctly I have to list all parameters and values in three places: the definition, the constructor function default args, and then again inside the new() command, where I also have to list all parameters in the right order of appearance in the definition. So I have three lists of the same parameters in the struct, such as:

mutable struct MyStruct
param1::Int64
param2::Float64
param3::Any
.
.
params100
function MyStruct(;param1=1,param2=2.0,... params100="g")
return new(param1,param2,...,params100)
end

This is a bit tedious for me. Is there a way to shorten this procedure?
Previously I’ve initialized these structs with name-value pairs using args… as input MyStruct(“param1”,10, “param2”,2.0, etc, and then had a parser inside the constructor, which shortened this to only two lists (definition, and defaults). But I want to use the native julia params1=x, param2=y, etc syntax. Is there a way to do so?
thanks!


#2

Have you looked at the package Parameters.jl?


#3

Hey, thanks @kristoffer.carlsson for pointing me to the Parameters.jl. I did not know about this package.
I had a bit of a play with it and indeed it saves me the duplications in construction.
But from what I could see, using @with_kw, the mutable struct cannot have zero argument constructor function, that is to say, I could not manipulate internally input parameters in the constructor after its call (e.g. param1==x ? do this : do that), the only thing closest to it was the assert in field definition, which is a bit limited. This is important when there is a relationship between parameters, but user only define a subset of them in the construction, where other should change accordingly.
So sorry,I should have explained it in the original post, that I want functions in the constructor working on input params.

I’ve also had a quick look at SimpleStructs.jl, and found it has same limitations as Parameters.jl in this respect.


#4

Maybe your constructor can accept a user defined Dictionary with values they want to set and using Parameters.jl otherwise. I don’t know if this solution will work, I will try later on.


#5

I think you are referring to something like https://github.com/mauro3/Parameters.jl/issues/52, maybe have a read there and add comments.


#6

Thanks @affans, the user defined parameter list is already in the struct definition. Therefore I think it won’t solve the duplication issue. At any case, as I’ve mentioned, I used to have a name value pair as input such as

MyStruct("params1",value1,"params2", value2)

and then in the constructor function I had a parse that would do

function MyStruct(args..) # no default values in function call
obj = new() # saves me from listing params  inside the new command
# list defaults below
obj.param1 =value1
...
obj.params100 = value100
# parse input name-value pairs
mod(length(args),2)==0 ? nothing : error("")
[setfield!(obj, Symbol(args[2*pIdx-1]),args[2*pIdx]) for pIdx in 1:length(args)/2] 

which then had me listing only two lists of params (definition and defaults) but the use of both “param” and params=, in the same code kind of bugged me, that the interface is not uniform.

It would be great to have a combination of Parameters.jl, which allows me to list default values in the definition, and somehow adjust the new() command, such that it will give an instance of a struct with the predefined default values. That is, if defaults are defined, just call new() with zero inputs.

This would reduce the procedure of initialization from having three lists to only having one.


#7

I can’t be certain about this without knowing the details of your problem, but I can’t escape the thought that one is unlikely to have 100 heterogeneous slots in a composite type without any pattern that would allow some organization into other containers.


#8

Also note that you can do:

@with_kw struct A
  a = 1
  b = a+1
  c = b+a
  @assert c==b+a # if you want to be sure that c is not set willy-nilly
  d = a==1 ? 4 : 8
end

Then, if you need to go beyond what is possible in above example, as you have a mutable struct, you can modify it after to your taste. Something like

@with_kw mutable struct B
....
end
function makeB(;kw...)
  out = B(;kw...)
  # do something with out
  ...
  return out
end