We are designing a generic interface for a library to take expectations of distributions , and would love some help trying to get the dispatch design correct. The basic idea is that there are a set of different algorithms (which we are writing) which can be applied to different types (which come from other libraries). There is a default algorithm that works for any of the types, but we want to change which is the default algorithm used based on the particular type. Lets start by defining some abstract types to help us out:
#These come from other libraries...
abstract type AbstractValue end
type Value1 <: AbstractValue end
type Value2 <: AbstractValue end
#These are in our package, and we can define these in whatever way is easiest.
abstract type AbstractAlgorithm end
type Algorithm1 <: AbstractAlgorithm end
type Algorithm2 <: AbstractAlgorithm end
The set of algorithms implementations is then a bunch of f(v::AbstractValue, alg::AbstractAlgorithm)
A few of the principles (which I will condense to a test-suite for the impatient) are
- There always needs to be a concrete AbstractValue (i.e., think of this as algorithms on Distributions.jl)
- For any
AbstractValue
without any further specialization, the algorithmAlgorithm1
always works, and we want it to be chosen by default - While
Algorithm1
is always possible for any distribution,Algorithm2
is only defined for someAbstractValue
. In particular, assume that it isn’t meaningful forValue2
- For a
Value1
we want to change the default algorithm toAlgorithm2
- There are different default parameters for
Algorithm1
andAlgorithm2
, which could have different names or values.- Assume that the default parameter is
N=10
forAlgorithm1
andN=5
forAlgorithm2
- It would be nice if Default values were by algorithm-value combination, but can live without it
- Assume that the default parameter is
- It is preferred if algorithm parameters use keyword arguments, but not strictly necessary
- We don’t need to worry about there being extra, unused, parameters
- It would be nice if we didn’t need to force a particular set of parameter names to be shared by Algorithms (i.e. use variable argument keywords)
- We expect to add in many more AbstractValue types, and a couple more Algorithm types
A Test Suite
using Base.Test
v1 = Value1()
v2 = Value2()
@test f(v1) == f(v1, Algorithm2, N=5) #i.e. defaults to Algorithm 2 with N=5 as the default
@test f(v1, N = 7) == f(v1, Algorithm2, N=7) #Can change the default value
@test f(v1, Algorithm1) == f(v1, Algorithm1, N=10) #can use use Algorithm1 (with the different default)
@test f(v2) == f(v2, Algorithm1, N=10) #i.e. uses algorithm 1 by default
@test f(v2, N = 8) == f(v2, Algorithm1, N=8) #Can change the default value
@test_throws f(v2, Algorithm2) #Not defined! Should throw
I tried to come up with something that would fulfill the test suite, but cannot figure out how to organize the functions, etc. to avoid ambiguity issues. Here was my (not passing the test) attempt
#Forwards based on default algorithm
f(v::AbstractValue, alg::Type{<:AbstractAlgorithm} = Algorithm1; kwargs...) = f(v,alg, kwargs...)
f(v::Value1, alg::Type{<:AbstractAlgorithm} = Algorithm2; kwargs...) = f(v,alg, kwargs...)
#Implementations
f(v::AbstractValue, alg::Type{Algorithm1}; N=10, kwargs...) = (1,N) #i.e. algorithm 1, N=N
f(v::Value1, alg::Type{Algorithm2}; N=5, kwargs...) = (2,N) #i.e. algorithm 2 specialization, N=N