Dispatch on function itself

Can Julia dispatch on the function itself?

test_func1(i::Int)=i+1
test_func2(i::Int)=i-1
(x::U{typeof(test_func1),typeof(test_func2)})(g::V)=println("ddd")

So the twos functions can do this:

test_func1([1,2,3])

"ddd"

test_func2([1,2,3])

"ddd"

I’m not sure what you trying to accomplish, but the following code works:

julia> struct F{T} <: Function
           f::T
       end

julia> (self::F)(x) = self.f(x)

julia> test_func1 = F(i::Int -> i + 1)
(::F{var"#3#4"}) (generic function with 1 method)

julia> test_func2 = F(i::Int -> i - 1)
(::F{var"#5#6"}) (generic function with 1 method)

julia> test_func1(1)
2

julia> test_func2(1)
0

julia> (self::F)(x) = println("ddd")

julia> test_func1([1, 2, 3])
ddd

julia> test_func2([1, 2, 3])
ddd

julia>
1 Like

I’m also not sure what you mean, but to answer your question:

Yes, Julia can dispatch on functions:

julia> f() = "f"
f (generic function with 1 method)

julia> g(::typeof(f)) = "I called $(f())"
g (generic function with 1 method)

julia> g(::Any) = "hello"
g (generic function with 2 methods)

julia> g(1)
"hello"

julia> g(f)
"I called f"
1 Like

Or maybe you’re trying to add a common method to two different functions, but you want to avoid redundant typing? In that case you could use @eval:

julia> test_func1(i::Int) = i + 1
test_func1 (generic function with 1 method)

julia> test_func2(i::Int) = i - 1
test_func2 (generic function with 1 method)

julia> for f in (:test_func1, :test_func2)
       @eval ($f)(g) = println("ddd")
       end

julia> test_func1([1,2,3])
ddd

julia> test_func2([1,2,3])
ddd
1 Like

Sort of. But the type of a function is not the same as the function signature, like it would be in C where you can supply a pointer to any function so long as the signature matches. Each individual function is a specific subtype of Function.

Multiple dispatch code selection happens when you call the supplied function, not when you call with it (although that sentence probably makes more sense in my head!).

However, you can despatch on the specific function

julia> f1(a::Int)::String = "f1"
f1 (generic function with 1 method)

julia> isa(f1, Function)
true

julia> typeof(f1)
typeof(f1)

julia> typeof(f1) in subtypes(Function)
true

julia> ff(f::typeof(f1), a::Int) = f1(a)
ff (generic function with 1 method)

julia> ff(f1, 1)
"f1"

julia> f2(a::Int)::String = "f2"
f2 (generic function with 1 method)

julia> typeof(f1) == typeof(f2)
false

julia> ff(f2, 2)
ERROR: MethodError: no method matching ff(::typeof(f2), ::Int64)
Closest candidates are:
  ff(::typeof(f1), ::Int64) at REPL[17]:1
Stacktrace:
 [1] top-level scope
   @ REPL[22]:1

julia> ff(f::Function, a::Int) = "f(a) = $(f(a))"
ff (generic function with 2 methods)

julia> ff(f2, 1)
"f(a) = f2"

julia> ff(f1, 1)
"f1"

and it gets more interesting if there are multiple methods of f1

julia> f1(a::Int, b::Int)::String = "f1 a b"
f1 (generic function with 2 methods)

julia> ff(f1::typeof(f1), a::Int, b::Int) = "f1(a,b) = $(f1(a, b))"
ff (generic function with 3 methods)

julia> ff(f1, 1)
"f1"

julia> ff(f1, 1, 2)
"f1(a,b) = f1 a b"

I’ve not encountered situations where this was useful, but I am sure there are occasions where specialising on supplied functions might be useful, due to implementation differences, e.g. thread safety or some sort of Abstract Type interface.

1 Like

while not exactly the same, you could do:

for f in (:test_func1,:test_func2)
  @eval $f(g::Vector) = println("ddd")
end

in this case, :test_func1 is a symbol, not the function itself.

1 Like