Consider two simple functions that take one Float64 and return another Float64:
julia> f(x) = x^2
f (generic function with 1 method)
julia> g(x) = x^3
g (generic function with 1 method)
Also, consider a higher-order function that takes a function and returns another function:
julia> h(fun::Function) = x -> 2fun(x)
h (generic function with 1 method)
julia> h(f)(2.)
8.0
julia> h(g)(2.)
16.0
Now, I want to apply h to an array of functions. However, such operation’s type stability depends on whether the array is homogeneous or not:
julia> fun_homog = [g, g]
2-element Array{#g,1}:
g
g
julia> fun_inhomog = [g, h]
2-element Array{Function,1}:
g
h
julia> @code_warntype broadcast(h, fun_homog)
(No instability)
julia> @code_warntype broadcast(h, fun_inhomog)
(Lots of instability)
The point here is that fun_homog’s element type is #g, whereas fun_inhomog’s element type is abstract Function.
I wonder if this kind of instability can be eliminated by allowing function type specification (like C’s function pointer). Clearly, f and g here are of the same type, in the sense that both take one Float64 and return another Float64. However, currently once an array [f, g] is formed, the array does not recognize such similarity between f and g.
In other words, I wonder if the array [f, g] can be typed as something like Array[::Function(::Float64)::Float64] to indicate that the array’s elements are functions that take one Float64 and return another Float64, and also wonder if such information could be used to eliminate the aforementioned type instability.