Should function-like objects be subtypes of Function?

For a function-like object, aka callable/functor, is there any problem with making it a subtype of Function?

As in,

struct Polynomial{R} <: Function  # XXX
    coeffs::Vector{R}
end

(p::Polynomial)(t) = evaluate_polynomial(p, t)   # see code in Julia docs

The point of this would be to be able to pass a Polynomial object as a Function argument, for example as the argument control in a function discretize defined as (among other methods)

function discretize(control::Function, tlist)
    return [control(t) for t in tlist]
end

Of course, I’m not saying that all callables must/should be subtypes of Function, but if they’re not subtypes of some other abstract type, is there a reason not to make them subtypes of Function?

One difference it can make is that subtypes of Function will not be automatically specialized by methods that pass it with the ::Function or ::Any annotation and do not call it even after inlining passes.* Of course if you want your callables to act more in line with Function’s design to limit overcompilation, then subtype it; that’s probably a good idea if a program has many functors passing through many call chains.

*Example:

julia> bar(op, a, b) = op(a, b);

julia> foo(op, a, b) = @noinline bar(op, a, b);

julia> foo(op::typeof(+), a, b) = @noinline bar(op, a, b);

julia> struct Minus end; (::Minus)(a, b) = a-b

julia> foo(-, 1, 1), foo(Minus(), 1, 1), foo(+, 1, 1)
(0, 0, 2)

julia> @which(foo(-, 1, 1))
foo(op, a, b) in Main at REPL[40]:1

julia> @which(foo(-, 1, 1)).specializations # Minus specializes, - does not
svec(MethodInstance for foo(::Function, ::Int64, ::Int64), MethodInstance for foo(::Minus, ::Int64, ::Int64), nothing, nothing, nothing, nothing, nothing, nothing)

julia> @which(foo(+, 1, 1))
foo(op::typeof(+), a, b) in Main at REPL[41]:1

julia> @which(foo(+, 1, 1)).specializations
svec(MethodInstance for foo(::typeof(+), ::Int64, ::Int64), nothing, nothing, nothing, nothing, nothing, nothing, nothing)
2 Likes