This is correct, as you observed. In this very particular example, you can use either of
foos = [Foo(Base.Fix2(^,2)), Foo(Base.Fix2(^,3))]
# Vector{Foo{Base.Fix2{typeof(^), Int64}}}
foos = [Foo(x->x^pow) for pow in 2:3] # or use `Base.Fix2` again
# Vector{Foo{var"#6#8"{Int64}}} # anonymous function closing over an `Int64`
which do have uniform types. As I showed here, sometimes this can be done using functors (such as Base.Fix2
or one you make yourself) or closures. Note the comprehension over x->x^pow
did produce a consistent function type because it was the same declaration that resulted in both, just capturing different values of the pow::Int64
in a closure.
Other times you might have to be more creative (for example, making [cos,sqrt]
stable doesn’t really work with this pattern). If they’re a fixed set of functions that doesn’t ever change, a tuple of functions (cos,sqrt)
can sometimes work. Other cases may be more challenging.
At the fully-flexible extreme, you can also look into FunctionWrappers.jl
for performantly wrapping different functions with matching type signatures. But I don’t have a lot of experience with that package so can’t help more.
You can typeassert the result of a function call
x = foos[2].func(3.14)::Float64
# you can additionally typeassert the `3.14::Float64`,
# but that doesn't change you anything if the compiler
# already knows the type of the input
This doesn’t fully resolve instability if foos
has differently-typed functions, but it means that the output is known to be Float64
(or else it will error) so any instability will not propagate.
Note that functions in Julia don’t have input/output types, so you can’t look at a function to see its signature. You can look at a function’s list of methods to see which (if any) corresponds to a particular set of input types (and also attempt to look at the resulting output type), but this isn’t usually something that’s done in Julia.
FunctionWrappers
, however, requires concrete input/output type declarations and can be used for this purpose. But again, I’ll recommend you try to do this without FunctionWrappers
if possible.