Pass arguments to struct constructor

You basically want to create your own macro here.

See Metaprogramming · The Julia Language

@with_kw @fill_params HYPname struct HHsol
    aP::Array{Float64,2}
    cP::Array{Float64,2}
end

To start let’s create an expression:

julia> e =:(
       struct HHsol
           aP::Array{Float64,2}
           cP::Array{Float64,2}
       end)
:(struct HHsol
      #= REPL[14]:3 =#
      aP::Array{Float64, 2}
      #= REPL[14]:4 =#
      cP::Array{Float64, 2}
  end)

julia> typeof(e)
Expr

julia> e.head
:struct

julia> e.args
3-element Vector{Any}:
 false
      :HHsol
      quote
    #= REPL[14]:3 =#
    aP::Array{Float64, 2}
    #= REPL[14]:4 =#
    cP::Array{Float64, 2}
end

julia> e.args[2]
:HHsol

julia> e.args[3]
quote
    #= REPL[14]:3 =#
    aP::Array{Float64, 2}
    #= REPL[14]:4 =#
    cP::Array{Float64, 2}
end

We see that we primarily want to edit argument 3 which is a quote block.

julia> e2 = e.args[3]
quote
    #= REPL[14]:3 =#
    aP::Array{Float64, 2}
    #= REPL[14]:4 =#
    cP::Array{Float64, 2}
end

julia> typeof(e2)
Expr

julia> e2.head
:block

julia> e2.args
4-element Vector{Any}:
 :(#= REPL[14]:3 =#)
 :(aP::Array{Float64, 2})
 :(#= REPL[14]:4 =#)
 :(cP::Array{Float64, 2})

julia> e2.args[3]
:(#= REPL[14]:4 =#)

julia> e2.args[2]
:(aP::Array{Float64, 2})

julia> e2.args[2].head
:(::)

julia> e2.args[2].args
2-element Vector{Any}:
 :aP
 :(Array{Float64, 2})

Now we need to figure how we want to modify this expression. To do so we create the expression that we want and examine it first.

julia> target = :(aP::Array{Float64, 2} = fill(1.0,HYPname.na, HYPname.nz))
:(aP::Array{Float64, 2} = fill(1.0, HYPname.na, HYPname.nz))

julia> target.head
:(=)

julia> target.args
2-element Vector{Any}:
 :(aP::Array{Float64, 2})
 :(fill(1.0, HYPname.na, HYPname.nz))

We see that all we want to do is replace the :: expression with a :(=) expression containing it. Let’s write a function to do that.

julia> function fill_individual_param(param_expr, s)
           Expr(
               :(=),
               param_expr,
               :(fill(1.0, $s.na, $s.nz))
           )
       end
fill_individual_param (generic function with 1 method)

julia> fill_individual_param(e2.args[2], :HYPname)
:(aP::Array{Float64, 2} = fill(1.0, ($(Expr(:escape, :HYPname))).na, ($(Expr(:escape, :HYPname))).nz))

julia> e2.args
4-element Vector{Any}:
 :(#= REPL[13]:3 =#)
 :(aP::Array{Float64, 2})
 :(#= REPL[13]:4 =#)
 :(cP::Array{Float64, 2})

julia> [isa(param, Expr) ? fill_individual_param(param, :HYPname) : param for param in e2.args]
4-element Vector{Any}:
 :(#= REPL[13]:3 =#)
 :(aP::Array{Float64, 2} = fill(1.0, ($(Expr(:escape, :HYPname))).na, ($(Expr(:escape, :HYPname))).nz))
 :(#= REPL[13]:4 =#)
 :(cP::Array{Float64, 2} = fill(1.0, ($(Expr(:escape, :HYPname))).na, ($(Expr(:escape, :HYPname))).nz))

Now we can use this replace the args of the quote block.

julia> e.args[3].args =  [isa(param, Expr) ? fill_individual_param(param, :HYPname) : param for param in e2.args]
4-element Vector{Any}:
 :(#= REPL[13]:3 =#)
 :(aP::Array{Float64, 2} = fill(1.0, ($(Expr(:escape, :HYPname))).na, ($(Expr(:escape, :HYPname))).nz))
 :(#= REPL[13]:4 =#)
 :(cP::Array{Float64, 2} = fill(1.0, ($(Expr(:escape, :HYPname))).na, ($(Expr(:escape, :HYPname))).nz))

julia> e
:(struct HHsol
      #= REPL[13]:3 =#
      aP::Array{Float64, 2} = fill(1.0, ($(Expr(:escape, :HYPname))).na, ($(Expr(:escape, :HYPname))).nz)
      #= REPL[13]:4 =#
      cP::Array{Float64, 2} = fill(1.0, ($(Expr(:escape, :HYPname))).na, ($(Expr(:escape, :HYPname))).nz)
  end)

We can then turn that into a function:

julia> function fill_params(e, s, m)
            e.args[3].args =  [isa(param, Expr) ? fill_individual_param(param, s) : param for param in e.args[3].args]
            return Parameters.with_kw(e, m, true)
       end
fill_params (generic function with 1 method)

julia> fill_params(
           :(struct HHsol
                  aP::Array{Float64,2}
                  cP::Array{Float64,2}
           end),
           :HYPname,
           @__MODULE__
       )
quote
Base.@__doc__ struct HHsol
            "Default: fill(1.0, HYPname.na, HYPname.nz)"
            aP::Array{Float64, 2}
            "Default: fill(1.0, HYPname.na, HYPname.nz)"
            cP::Array{Float64, 2}
            HHsol(; aP = fill(1.0, HYPname.na, HYPname.nz), cP = fill(1.0, HYPname.na, HYPname.nz)) = begin
                    #= C:\Users\kittisopikulm\.julia\packages\Parameters\MK0O4\src\Parameters.jl:493 =#
                    HHsol(aP, cP)
                end
            HHsol(aP, cP) = begin
                    #= C:\Users\kittisopikulm\.julia\packages\Parameters\MK0O4\src\Parameters.jl:505 =#
                    new(aP, cP)
                end
        end
    ()
    ()
    begin
        HHsol(pp::HHsol; kws...) = begin
                (Parameters).reconstruct(pp, kws)
            end
        HHsol(pp::HHsol, di::(Parameters).AbstractDict) = begin
                (Parameters).reconstruct(pp, di)
            end
        HHsol(pp::HHsol, di::Vararg{Tuple{Symbol, Any}}) = begin
                (Parameters).reconstruct(pp, di)
            end
    end
    function Base.show(io::IO, p::HHsol)
        if get(io, :compact, false) || get(io, :typeinfo, nothing) == HHsol
            Base.show_default(IOContext(io, :limit => true), p)
        else
            dump(IOContext(io, :limit => true), p, maxdepth = 1)
        end
    end
    macro unpack_HHsol(ex)
        esc((Parameters)._unpack(ex, Any[:aP, :cP]))
    end
    begin
        macro pack_HHsol()
            esc((Parameters)._pack_new(HHsol, Any[:aP, :cP]))
        end
    end
    HHsol
end

Now we can turn that function into a macro:

julia> macro fill_params(s, e)
           esc(fill_params(e, s, __module__))
       end
@fill_params (macro with 1 method)

julia> @macroexpand @fill_params(HYPname,struct HHsol
           aP::Array{Float64,2}
           cP::Array{Float64,2}
       end)
quote
    begin
        $(Expr(:meta, :doc))
        struct HHsol
            "Default: fill(1.0, HYPname.na, HYPname.nz)"
            aP::Array{Float64, 2}
            "Default: fill(1.0, HYPname.na, HYPname.nz)"
            cP::Array{Float64, 2}
            HHsol(; aP = fill(1.0, HYPname.na, HYPname.nz), cP = fill(1.0, HYPname.na, HYPname.nz)) = begin
                    HHsol(aP, cP)
                end
            HHsol(aP, cP) = begin
                    new(aP, cP)
                end
        end
    end
    ()
    ()
    begin
        HHsol(pp::HHsol; kws...) = begin
                (Parameters).reconstruct(pp, kws)
            end
        HHsol(pp::HHsol, di::(Parameters).AbstractDict) = begin
                (Parameters).reconstruct(pp, di)
            end
        HHsol(pp::HHsol, di::Vararg{Tuple{Symbol, Any}}) = begin
                (Parameters).reconstruct(pp, di)
            end
    end
    function Base.show(io::IO, p::HHsol)
        if get(io, :compact, false) || get(io, :typeinfo, nothing) == HHsol
            Base.show_default(IOContext(io, :limit => true), p)
        else
            dump(IOContext(io, :limit => true), p, maxdepth = 1)
        end
    end
    macro unpack_HHsol(ex)
        esc((Parameters)._unpack(ex, Any[:aP, :cP]))
    end
    begin
        macro pack_HHsol()
            esc((Parameters)._pack_new(HHsol, Any[:aP, :cP]))
        end
    end
    HHsol
end

We can then use it as follows.

julia> HYPname = (; na = 5, nz = 3)
(na = 5, nz = 3)

julia> @fill_params(HYPname,struct HHsol
           aP::Array{Float64,2}
           cP::Array{Float64,2}
       end)
HHsol

julia> HYPname = (; na = 5, nz = 3)
(na = 5, nz = 3)

julia> HHsol()
HHsol
  aP: Array{Float64}((5, 3)) [1.0 1.0 1.0; 1.0 1.0 1.0; … ; 1.0 1.0 1.0; 1.0 1.0 1.0]
  cP: Array{Float64}((5, 3)) [1.0 1.0 1.0; 1.0 1.0 1.0; … ; 1.0 1.0 1.0; 1.0 1.0 1.0]


julia> HYPname = (; na = 4, nz = 9)
(na = 4, nz = 9)

julia> HHsol()
HHsol
  aP: Array{Float64}((4, 9)) [1.0 1.0 … 1.0 1.0; 1.0 1.0 … 1.0 1.0; 1.0 1.0 … 1.0 1.0; 1.0 1.0 … 1.0 1.0]
  cP: Array{Float64}((4, 9)) [1.0 1.0 … 1.0 1.0; 1.0 1.0 … 1.0 1.0; 1.0 1.0 … 1.0 1.0; 1.0 1.0 … 1.0 1.0]```
3 Likes