Dealing with multiple dispatch `Vararg`s

Say that we have two types:

struct A end  #= other library =#
struct B end  #= my library    =#

Now, let’s assume that in some other library there is a method:

foo(x, xs...) = 1
foo(x::A, xs::A...) = 1

I want to overload this method to return 2, for situations where any of (x, xs...) is type B.

In theory, this should be valid in the method hierarchy, right? I just can’t figure out how to actually express it. Is there a way I can do this without causing ambiguities or committing piracy?

So far I have been doing

foo(x::B, xs...) = 2
foo(x, x2::B, xs...) = 2
foo(x, x2, x3::B, xs...) = 2

which correctly captures cases where any of the first three arguments is B:

julia> foo(B())
2

julia> foo(A(), B())
2

julia> foo(A(), A(), B())
2

But past that point, we hit the generic method and get 1.

julia> foo(A(), A(), A(), B())
1

So what are my options, other than defining up to some max number of arguments? I tried

julia> foo(x, xs::Union{Any,B}...) = 2

but this appears to be piracy, and overwrites the original f(x, xs...) method.

Maybe this isn’t possible?


As to why I want this: in BorrowChecker.jl, I wish to overload the method

Base.rand(rng::AbstractRNG, d::Integer, dims::Integer...)

for cases where at least one of (d, dims...) is type AllWrappers{<:Integer} (this AllWrappers type alias is a union of Borrowed and Owned wrapper objects). I could define all the permutations up to some integer, but seems like there should be a better way. Or maybe the answer is simply “don’t do this”… Or use Cassette.jl (now broken, sadly)… but, I think it would be nice to be able to overload methods like this.

Unfortunately you can’t without resorting to traits or whatever (which doesn’t help you if you don’t own the function being overloaded). There’s an open issue for it here: Dispatch on a type being anywhere in a signature · Issue #39878 · JuliaLang/julia · GitHub

1 Like