Argument type of higher order functions

If I define:

function myfunc(f::T, g::T; b=2.5 ) where T<:Function
    #some lines here
    h(t) = b*(f(t) +g(t)^2)
end
f(t; a=2)=a*t
g(t; a=3)=a+exp(t)
h=myfunc(f, g)
h(1.3)

it is thrown the error:

MethodError: no method matching myfunc(::typeof(f), ::typeof(g))
Closest candidates are:
  myfunc(::T, ::T; b) where T<:Function at In[2]:1

Stacktrace:
 [1] top-level scope
   @ In[2]:7

but the following two definitions work:

function myfunc(f::Function, g::Function) 
    #...
    h(t) = f(t) +g(t)^2
end
h=myfunc(f,g)
h(1.3)
function myfunc(f::T) where T<:Function
    #...
    h(t)=exp(f(t))-f(t)
end
k=myfunc(t->-t^2)
k(4)

Why the last one works, but the first one doesn’t?

First version is enforcing that both functions share 1 unknown type T, but f and g have different types. Try function myfunc(f::T, g::S; b=2.5 ) where {T<:Function, S<:Function}

3 Likes

In case it isn’t obvious to you why this is so, as stated here in the Julia documentation, every function in Julia has a distinct type.

3 Likes

Technically, the first version could’ve worked with T == Function. Then, both f::T and g::T, and T <: Function.
The reason why it doesn’t actually work is that Julia implicitly constrains T to be a concrete type in this case.

Both arguments need to be of the same type, and due to only concrete types of the passed in objects (instead of their abstract super types) being used in dispatch, the constraint that the types must be the same results in dispatch saying “the types don’t match, ergo there’s a MethodError”. Runtime objects can never have an abstract type.

Julia doesn’t implicitly constrain T to be a concrete type - it’s the concreteness of the types of the objects that results in the MethodError.