Function type specification


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.)

julia> h(g)(2.)

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}:

julia> fun_inhomog = [g, h]
2-element Array{Function,1}:

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.



Glad to know that there is already an effort in this direction!

How can I use this in the above case? Could you show an example?


Why wasn’t function typing part of Julia’s design from the start ?

I find it really surprising that something like


doesn’t already exist.

Isn’t functionwrappers a hack to get around what is really a design problem ?


Because it has problematic interactions with multiple dispatch:


The biggest challenge right now to doing something like this is that the type of those functions isn’t ::Function(::Float64)::Float64, they are ::Function(f, ::Float64)::Float64 and ::Function(g, ::Float64)::Float64. For another recent post on the topic, see Proposal for a first-class dispatch wrapper.


That proposal looked really nice, are the plans in this direction?


Seems plausible, but the devil’s in the details and actually implementing it. Fortunately @jameson has a solid track record of delivering on even harder projects than this :nerd:

That said, this is a complex issue and we’ll have to think it through pretty carefully.