Does method2 have a fallback method that would be called in the case no special implementation is provided for the type? If not you could try using method_exists.
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:
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.
the second line generates: @generated SimpleTraits.trait{F}(::Type{HasJac{F}}) = __has_jac(F) ? :(HasJac{F}) : :(Not{HasJac{F}}). This works for any function some_check(T)::Bool.
just some random thoughts, not comprehensive answer.
one usual solution i keep seeing in julia source, is creating a default for the abstract type, and either giving some fallback behavior, or just returning ânothingâ.
another usual solution is to give info on the actual type, for example iteratorsize which gives back enum values HasLength() | HasShape() | IsInfinite() | SizeUnknown(). this way an implementor can communicate attributes of a subtype back to the common code. the common code decides actions based on this value.
One reading of the original question is whether the specificmethod2 for a subtype of Foo (e.g., Bar) exists, as opposed to a method for Foo or an intermediate abstract type. This may be tested with
:method2 in [m.name for m in methodswith(Bar)]
Other replies seem to accept the more general methods, and probably they fit your needs, but perhaps this expression will help anyone who (mis-)reads the question as I did. The generality applies to method_exists and applicable, but I donât understand how type hierarchy interacts with traits.
While I am at it, I will express the hope that @ChrisRackauckas will find the time to write a blog post about traits, since (a) he is one of the few package writers using them, (b) he writes good blog posts, and (c) his real-world use cases may guide the developers to optimize the implementation of traits in the language proper.
For the sake of anyone who reads this, we are pursuing a method_exists-based scheme with some macros to reduce boilerplate code. Our solvers will check if the userâs POMDP type has the required functions like this:
function POMDPs.solve{S,A,O}(s::CoolSolver, p::POMDP{S,A,O})
# register requirements
@check_requirements "CoolSolver" begin
PType = typeof(p)
@req discount(::PType)
@req states(::PType)
@req actions(::PType)
@req transition(::PType, ::S, ::A)
s = first(states(p))
a = first(actions(p))
t_dist = transition(p, s, a)
@req rand( ::AbstractRNG, ::typeof(t_dist))
end
# do the work of solving the pomdp
# ...
end
which will output something like this if rand is missing