I thought I would build up a data structure with intermediate values I needed for a computation, to which I would later add the final results and return the whole thing. I’ve attempted to add type information, but some of the types are themselves incomplete, and some of the types involve data that is created after I create the results structure.
The first issue is whether I should be trying to do this at all. An alternative would be to create one type that holds intermediate data and another that holds the later results, including a reference to the intermediate data. This seems a little clunky because the consumer of the result then gets some values by going 2 levels down and some by going 1 level down. Of course, that could be hidden with accessor functions (tedious to program) or the flattening could be done at the final creation of the result object (also tedious, and creating considerable redundancy).
I tried
abstract type AbstractResult end
mutable struct SimpleResult{T <: Real, AllComboType} <: AbstractResult
β::NamedVector{T} # coefficents
se::NamedVector{T} # std errr of coefficients
vcv::NamedMatrix{T} # variance covariance matrix
H::NamedMatrix{T} # Hessian
r::Optim.OptimizationResults # results of optimization
nms # names of individual terms, likely strings, possibly symbols
### below here are intermediate values for the calculation
mm # rhs model (design) matrix, though may not literally be a Matrix
allcombo::AllComboType # all combinations of categorical variables in outcome
s # schema
ts # terms after applying schema
w::Vector{T} # weights, normalized to sum to sample size
function IntermediateSimpleResult{T}(mm, allcombo::AllComboType, s, ts)
a = new{T, AllComboType}()
a.mm = mm
a.allcombo = allcombo
a.s = s
a.ts = ts
return a
end
end
The compiler says
LoadError: UndefVarError: IntermediateSimpleResult not defined
Raising a lot of questions:
- Does the inner constructor need to have the same name as the type?
- How to handle the relation between type information for the inner constructor and the type? Should I repeat the types for the inner constructor? Is the implicit useage in the above code OK, i.e., AllComboType is inferred from the argument to the inner constructor? Can I omit the types entirely from the inner constructor as they are implicitly inherited form the
struct
? There are quite a few spots type information could go (thestruct
definition; the inner constructor definition, the invocation ofnew
inside the inner constructor, and the invocation of the inner constructor from somewhere out in the program) and I’m not sure which are required/allowed/prohibited. - Does it matter if the type names are the same or different between the type and the inner constructor? Parametric inner constructor inherits parameter from global scope indicates some surprises with the namespace used for type parameters.*
- I’m concerned that I don’t have the specifics of, e.g., the OptimizationResults at hand (it’s an abstract type), so that even if I get past these initial problems that will be a hang-up. Does the system require concrete types for everything before creating a compound object?
By the way, one reason I don’t want to pass around tuples is that I want to dispatch on the type of the object for my intermediate calculations.
* I think I would expect that each parameter in a parametric type, e.g., T
, is treated as a new symbol within the scope for which it applies. So inside mutable struct SimpleResult
, T
is a new type, unrelated to any uses outside the scope. Apparently that’s not how it works. The rule I was expecting would also imply that the T
used in the function IntermediateResult
would in turn hide the T
used in the structure definition. That would make it hard to link the two T
’s, which may be one reason it doesn’t work as I expect.