Suppose I am trying to define a hvcat for a user type, and after I deal with the first argument (which can be an Int, or a Tuple{Vararg{Int}), the methods share a lot of code. So it would be ideal for me to just branch on the first argument and then implement one method.
But ambiguities do not allow that. MWE (for just this part):
julia> struct Foo end
julia> Base.hvcat(blocks_per_row::Union{Tuple{Vararg{Int}}, Int}, foos::Foo...) = Foo()
julia> [Foo() Foo();
Foo() Foo()]
ERROR: MethodError: hvcat(::Tuple{Int64, Int64}, ::Foo, ::Foo, ::Foo, ::Foo) is ambiguous.
Candidates:
hvcat(rows::Tuple{Vararg{Int64}}, xs...)
@ Base abstractarray.jl:2155
hvcat(blocks_per_row::Union{Int64, Tuple{Vararg{Int64}}}, foos::Foo...)
@ Main REPL[37]:1
Now I could extract the core logic of hvcat which does the actual heavy lifting (elided above) to an auxiliary function and have the two methods call that, but I am wondering if there is an easier way.
hvcat(::Int, ...) has a generic fallback so strictly speaking I do not need to define both methods, but this undocumented so I am not sure if this is something I can rely on).
which form actually lowers to hvcat(::Int, ...)? Everything I tried lowers to the other method with the tuple.
I think it’s weird that hvcat takes a tuple of integers for each row in the first place when all the integers must be equal. This would be a lot simpler to extend if hvcat instead took a Matrix shape Tuple{Int, Int} the lowerer figures out.
Thank you for the correction. I should’ve checked that before, that was dumb of me.
I’ve been experimenting and searching for solutions, and it seems there’s no way of resolving the ambiguity issue when using Unions.
Did find a somewhat better solution, using @eval to generate methods for each types.
for T in (:(Tuple{Vararg{Int}}), :Int)
@eval Base.hvcat(blocks_per_row::$T, foos::Foo...)=Foo()
end
I guess this is the next best thing you can try when Unions can’t do the job.
Since then I discovered that there is a fallback in Base method that handles the ::Int case, and in any case, nothing lowers to that. So it is enough to write the ::Tuple{Vararg{Int}} method.