I am writing a data type that is, essentially, a wrapper for a function, which will be accessed as a functor, but the arguments for the outside struct constructor determine the nature of the function.
(As an irrelevant aside, the goal is to be able to populate an array with “data generating process” data structures describing stochastic processes. The array of types will support estimation of parameters (via indirect inference / simulated method of moments and other methods); it will support calibration of subsets of parameters; and it will support counterfactual simulations, where exogenous variables are set to choice values. Frequently, when people estimate this sort of model, they put parameters and variables in arrays with no way to keep track of which position represent which variable/parameter except through mental effort. Requiring mental effort to keep track of this sort of thing invites serious and undetectable semantic errors).
The struct looks like:
struct DataGeneratingProcess <: AbstractDataGeneratingProcess
LHS_varname::Union{Symbol, Vector{Symbol}}
RHS_varnames::Vector{Symbol}
parameter_names::Vector{Symbol}
func::Function
func_print::String
end
The outer constructor below is a work-in progress and I can’t get it to do what I want it to do.
The function _f( . ) need to have a method that accept a pair of NamedTuple’s, where the type signature of the NamedTuple’s depend on arguments of the constructor. Then I want to add other methods to this function. Ignoring the wisdom of this general approach, how do I execute on this?
I have tried many different ways to execute on this, but I either can’t add methods to the function or I can’t metaprogram the type signature of the function.
function DataGeneratingProcess(lhs::Union{Symbol, Vector{Symbol}}, rhs::Vector{Symbol}, parameter_names::Vector{Symbol}, eqn::String)
#rhs = sort(rhs; rev=true)
#parameter_names = sort(parameter_names; rev=true)
RhsType = _vec_to_nt(rhs)
ParmType = _vec_to_nt(parameter_names)
parsed_eqn = split(eqn, "=")[2] #TODO: better parsing than relying on spaces.
for v in rhs
parsed_eqn = replace(parsed_eqn, " " * string(v) * " " => " inner_rhs[:$v] ")
end
for v in parameter_names
parsed_eqn = replace(parsed_eqn, " " * string(v) * " " => " inner_params[:$v] ")
end
#_f(inner_rhs::Union{Tuple, AbstractArray}, inner_params::Union{Tuple, AbstractArray}) = _f(zip(rhs, inner_rhs), zip(parameter_names, inner_params))
# let RhsType=RhsType, ParmType=ParmType, parsed_eqn=parsed_eqn, parameter_names, rhs
# func_string = """
# function _f(inner_rhs::$RhsType, inner_params::$ParmType)
# return $parsed_eqn
# end
# """
# print(func_string)
# func_string |> Meta.parse |> Meta.eval
#
# end
func_string = """
global function _f(inner_rhs::$RhsType, inner_params::$ParmType)
return $parsed_eqn
end
"""
#print(func_string)
func_string |> Meta.parse |> Meta.eval
function _f(rhs_var::AbstractVector{N} where N <: Number, param_vals::AbstractVector{N} where N <: Number)
return rhs_var
end
function _f()
return nothing
end
return DataGeneratingProcess(lhs, rhs, parameter_names, _f, eqn)
end
The below should run but does not. I can either get a single method function generated through metaprogramming that can evaluate on the named tuple or I can get a function with multiple methods that can evaluate on the pair of arrays but not one that can run on the NamedTuple generated type-signature.
dgp1 = DataGeneratingProcess(:y, [:x, :w], [:β, :γ],"y = β * x + γ * w ")
dgp1.func((x=1,w=1), (β=1, γ=3))
dgp1.func([1,2], [1, 2])