You can use traits for this:
abstract Match
abstract NotMatch
"If the two arguments match, i.e. they can work together, then it returns Match otherwise NotMatch."
function ismatch end
ismatch(::Type{B1}, ::Type{B2}) = Match
ismatch(::Type{C1}, ::Type{C2}) = Match
ismatch(::Any, ::Any) = NotMatch # catch-all defaults to NotMatch
# Now write your function like so:
quite_general{T<:A1, S<:A2}(p::T, q::Vector{S})::T = _quite_general(ismatch(T,S), p,q) # trait-dispatch
_quite_general(::Type{Match},p,q) = ... # some logic
_quite_general{T<:A1, S<:A2}(::Type{NotMatch}, p::T, q::Vector{S}) = error("Type $T and $S don't match")
So for each type combination you have to specify whether they match or not with ismatch
(but just this once). Then each function which does something according to the match, you have to split up into the trait-dispatch and the logic function. There is a new manual section about this: julia/methods.rst at 9bad705a41ab68b54b0ded18eb3c386e187ad45a · JuliaLang/julia · GitHub (not merged yet thus not well rendered) and there is the package SimpleTraits.jl which provides macro-sugar for above pattern.