I have the feeling I’m overdoing it / not doing the right thing with types. I’m wrapping ODESolutions coming from OrdinaryDifferentialEquations for downstream use like this:
struct SolSR_FS{T<:Real,O,B} <: SolSR
sol::O
bd::B
tspan::Tuple{T,T}
tjumps::Vector{T}
end
struct SS_Data{N,T<:Real,V<:Real,O,B}
# this mass of type parameters sure is ugly. but it does improve performance
# for S_to_end! Not clear what the most compact way would be to achieve
# performance.
sol :: SolSR_FS{T,O,B}
ts :: SVector{N,T}
R_steps :: SVector{N,V}
lambda_emR_steps :: SVector{N,V}
end
Here the T is the time type and V the value element type of the solution, and I introduced O and B solely because they make a downstream function f(::SS_Data, args...) faster and eliminate any red lines in @code_warntype
But I feel this ugly zoo of types must be at least partially unnecessary? Is there a better way to tell downstream that the output sol(t) is of a known type?
Yes, unless you know the exact type of O or B beforehand it’s best to keep them as parameters. Even if you did know their type beforehand in your specific case, having them as parameters makes your code more flexible.
struct ODESolution{T, N, uType, uType2, DType, tType, rateType, discType, P, A, IType, S,
AC <: Union{Nothing, Vector{Int}}, R, O, V} <:
AbstractODESolution{T, N, uType}
u::uType
u_analytic::uType2
errors::DType
t::tType
k::rateType
discretes::discType
prob::P
alg::A
interp::IType
dense::Bool
tslocation::Int
stats::S
alg_choice::AC
retcode::ReturnCode.T
resid::R
original::O
saved_subsystem::V
end
Almost every field has its own type parameter. But you don’t need to write these out in your definition of SolSR_FS, in the same way you don’t need to write out the type parameters of SolSR_FS in the definition of SS_Data.
Of course, sometimes it is useful to (partially) write out the signature, for example if you want to enforce constraints on type parameters (e.g. if you wanted to make sure some number types are equal.)