Using Module names for dispatch

I’m working on a metapackage (HermitianDiagonalization.jl) that implements a bunch of methods based on the functionality of some third-party packages (Arpack, ArnoldiMethod, etc…), provided they have been loaded by the user. I’m using Requires.jl for this. There is one bit of functionality I’m struggling to get working.

I want to write a function diagonalizer(matrix, packagename::Module) that does different things on a matrix depending on packagename of a loaded package, or throw an error if the package was not loaded. How can I achieve this? (I would also appreciate an answer of the sort “you’re approaching this completely wrong, try this instead”). Thanks!

What I tried

I thought about using Julia’s dispatch machinery. Now, packagename isa Module, such as Arpack. I cannot dispatch on different packagenames, because they are not different types themselves, they are just different objects of type Module. I could just try to use an if-elseif construct, such as

if packagename == Arnoldi
    ...
elseif packagename == KrylovKit
    ...
...
end

But of course this doesn’t work, because the symbols Arnoldi, KrylovKit etc need not be defined in any given session (if the user didn’t load them). I also tried building a defaultmethods = Dict{Module,Function} as part of the Requires machinery, but if the module is loaded after loading my package, it does not trigger push! of the new module into defaultmethods, so no dice… I cannot seem to figure this one out!

I believe the old method used to be to dispatch on Val(:something) but now with constant propagation you can just do diagonalizer(A,:arpack) and check with an if in your function. The check will be compiled away and the method will be type-stable.

So there is no way to use Arpack (the Module name itself), instead of :arpack?

I found it quite appealing and simple to have an API like using Arpack; diagonalizer(m, Arpack)… No need to remember/find out extra information on valid arguments…

You can see for yourself what you can do with a module name:

julia> methodswith(typeof(Base))

There’s a number of things there, but in particular fullname should get you what you want.

2 Likes

Nice one! So something like the following, yes? Thanks!

function diagonalizer(a, m::Module)
   name = first(fullname(m))
   if name == :Arpack
   elseif name == ...
   ...
   end
end

If you want to be able to dispatch on the module name, this should work:

function diagonalizer(a, m::Module)
   diagonalizer(Val(first(fullname(m))))
end
function diagonalizer(a, m::Val{:Arpack})
  # Do Arpack-specific things
end
# more dispatches go here...
2 Likes

@jpsamaroo, that’s exactly what I ended up doing. Constant propagation even makes it inferable. Thanks to both, this community is amazing.

EDIT: my bad, I’m afraid inference is not able to figure this out by constant prop; looking into it.

1 Like