How to find if a method is implemented for an interface?

method_exists and applicable may work depending on the use case. But note that checking method_exists happens at runtime, which may not be what you want. Here’s a more complicated but possibly more performant option (depending on whether compile-time enhancements out-weigh the cost of compilation) by mixing method_exists with traits.

In JuliaDiffEq, our interface for providing extra functions is by adding additional dispatches. Because this was very important to us, we came up with a way to check for the existence of dispatches that will actually work at compile time (i.e. if you check the @code_llvm, the function has_jac(f) (checks if the Jacobian dispatch exists) will compile down to a true/false, and in codes which use if has_jac(f) the inappropriate branch will compile away and no checking needs to be done at runtime).

You can see the PR with how we came about the solution here:

It makes use of @mauro3 's SimpleTraits.jl. Let me spell it out in more detail since it’s a little tricky.

@mauro3 provided the code which uses method_exists to see if a method exists by checking the first arg for a certain type (you can see in the docs that our interface has a value-type in front for each “extra dispatch”)

check_first_arg(f,T::Type) = check_first_arg(typeof(f),T)
function check_first_arg{F}(::Type{F}, T::Type)
    typ = Tuple{Any, T, Vararg}
    for m in Base.MethodList(F.name.mt) # F.name.mt gets the method-table
        m.sig<:typ && return true
    end
    return false
end

However, if you @code_llvm that, you’ll see that (because I think the MethodList is global?) that the code is huge and it actually is not as fast as you’d hope. So then we use an internal function:

__has_jac(f) = check_first_arg(f, Val{:jac})

to check for the dispatch, and use that internal function to apply a trait:

@traitdef HasJac{F}
@generated SimpleTraits.trait{F}(::Type{HasJac{F}}) = __has_jac(F) ? :(HasJac{F}) : :(Not{HasJac{F}})

Since checking for traits is a compile-time check, the following is then what we really wanted:

has_jac{T}(f::T)   = istrait(HasJac{T})

In the end, @code_llvm compiles has_jac(f) to either be a true or a false, and everything works at compile time.

Of course, this can be major overkill depending on your use case. However, we found this solution useful since it allows us to have different branches (and dispatches due to traits) which fully optimize and are type-stable thanks to the fact that the existence of the method ends up as compile-time information.

5 Likes