Don’t use getindex
with runtime integers i
(x[i]
) if you want it to be type-stable. Instead, you need all of the those integers to be known at compile-time. So
for i = 1:3
x[i]
end
are not type stable calls for an inhomogenous tuple, while
x[1]; x[2]; x[3]
is. Two ways to handle this. One is a metaprogramming: generated functions work nicely. But another way is recursion. I’m thinking of writing a blog post to describe it in full, but an example of how to do this is found here:
https://github.com/JuliaDiffEq/OrdinaryDiffEq.jl/blob/master/src/callbacks.jl#L195
In this example, apply_discrete_callback!(integrator,callback)
needs to be called on every callback in a tuple (and each callback is differently typed). So the basic call is:
apply_discrete_callback!(integrator,discrete_callbacks...)
This then gets “caught” by function headers like
apply_discrete_callback!(integrator::ODEIntegrator,callback::DiscreteCallback,args...)
where callback
is the first callback in the list and args
is the rest. You can then recrusively do something on callback
and pass args
into apply_discrete_callback!
, and it’ll get smaller an smaller. Manage the Base cases and you’re good: no getindex
was ever needed.
Then, for this code, the compiler will inline this into one giant call. It essentially makes it amount to:
apply_discrete_callback!(integrator,callback[1])
apply_discrete_callback!(integrator,callback[2])
apply_discrete_callback!(integrator,callback[3])
...
knowing what the index is at compile time and thus able to infer each call and be type-stable. The only cost here is the compile time, but if you’re keeping the tuples around and repeatedly calling this, it’ll only have to compile this full recursive routine once for the the tuple.