Correctly dispatching on a variable number of 2-tuples with mixed types

I am currently trying to come up with a function signature that would let me take an arbitrary number of 2-tuples where the types of the elements are not necessarily the same. I know that with the signature

function f(dom::Vararg{Tuple{T, T}, D} where {D, T <: Real}
   ...
end

I can dispatch on any number of tuples as long as their elements are all the same type. For instance, (-2.0, 2.0), (-1.0, 1.0) and so on.

I would like to catch elements of the form (-1, 1.0), (1, 1.0) for instance. If only to explicitly throw an argument error, but preferably to promote all the elements and call the type-stable method defined earlier.

I have tried with this signature

function f(dom::Vararg{<:Tuple{Real, Real}, D} where {D <: Real}
    ...
end

and

function f(dom::Vararg{<:Tuple{<:Real, <:Real}, D} where {D <: Real}
    ...
end

But I get this error when trying to call it

julia> f((1.0, 1), (1.0, 1.0)

ERROR: MethodError: no method matching f(::Tuple{Float64, Int64}, ::Tuple{Float64, Float64})

Closest candidates are:
    f(::Tuple{Real, Real}...) where D <: Real
      @ REPL
    f(::Tuple{T, T}...) where {D, T <: Real}
      @ REPL

I have read the documentation on diagonal types, but I couldn’t come up with the correct signature myself.

Not sure whether this does what you want:

# 2-element tuples of various types -> promote them to common type
function foo(dom::Vararg{Tuple{Any,Any}})
    common_type = reduce(promote_type, promote_type(typeof(t[1]), typeof(t[2])) for t in dom)
    @info "" common_type
    foo(((convert(common_type, t[1]), convert(common_type, t[2])) for t in dom)...)
end

# all tuples have the same type
function foo(dom::Vararg{Tuple{T, T}}) where T
    @info "final" T
end

Now you can do:

julia> foo((1,1),(1,1))
┌ Info: final
└   T = Int64

julia> foo((1,1),(1,1.0))
┌ Info: 
└   common_type = Float64
┌ Info: final
└   T = Float64

I think this fails with mixed non-numeric tuples, like (1.0, “1”), perhaps it works by making it Tuple{Real, Real}, since the function is expecting numbers after all

Thanks especially for the common_type function, I was uncertain of how to do that

What is the desired behavior for a tuple like (1,"1")? Use parse(numeric_type, "1")?
Honestly I think at some point you should put the burden on the user of this function to provide sane types instead of trying to handle every possible type you can think of :sweat_smile:

You might be right :sweat_smile:, I was trying to catch all those wrong combinations to fail gracefully, but It makes more sense to document it