I was watching @kristoffer.carlsson’s JuliCon talk on Package Extensions and I was quite interested in the discussion about “proving” dependencies to ensure that a user explicitly loads the package for which there is a package extension.
The code snippet given in the talk is
function solve_system(A, x; alg::Module)
ext_hypre = Base.get_exension(@__MODULE__ :HYPREExt)
if ext_hypre !== nothing && alg == ext_hypre.HYPRE
_solve_system(HYPRESolver(), A, x)
elseif alg == ...
# ...
end
end
making users pass a module as alg
to ensure that they’ve loaded the dependency explicitly.
I was wondering if there was a recommended pattern for generalizing this code to use multiple dispatch for choosing the solver. This would be to avoid the potentially long if-elseif-chain and to allow users to define their own optimization methods in their own (non-extension) modules.
The example snippet addresses the exact situation that I have in QuantumControl.jl (or actually, QuantumControlBase.jl
) where currently, I’m dispatching on symbols for the optimization method, as
optimize(problem; method) = optimize(problem, method) # keyword arg (public API)
optimize(problem, method::Symbol) = optimize(problem, Val(method))
and then e.g. in Krotov.jl, I have
optimize(problem, method::Val{:krotov}) = optimize_krotov(problem)
I might want to move that definition from Krotov.jl
to an extension module of QuantumControl.jl
so that users don’t necessarily have to install all the packages for the various methods to use QuantumControl.jl
with just one method. At the same time, I’d like to keep the ability for someone to have their own method in a package I don’t know anything about, and extend optimize
in a consistent fashion.
Unfortunately, I don’t think there’s any way to directly dispatch on a module, since typeof(SomeModule)
is Module
. It would have been nicer if typeof(SomeModule)
was a singleton sub-type of Module
(just like typeof(some_function)
is a subtype of Function
), but without that, it seems to me the easiest way to implement it is to simply add
optimize(problem, method::Module) = optimize(problem, Val(nameof(method)))
That is, dispatch on the name of the module as a symbol.
Are there any better way to more directly dispatch on a Module?