How to deal with parameters of the type Vector{Tuple}?

Hi,

I’m writing some code where I expect a vector of 2-element tuples but I can’t figure out how to type the parameters in such a way that it’s clear what needs to be passed but not so restrictive that type promotion is thrown out of the window.

I thought the following would work (I simplified the code to the minimum):

``
f(v::Vector{Tuple{Real, Real}}) = v

f([(1, 1)])


But this results in the following error:

ERROR: MethodError: no method matching f(::Array{Tuple{Int64,Int64},1})
Closest candidates are:
f(::Array{Tuple{Real,Real},1}) at none:1
Stacktrace:
[1] top-level scope at none:1


So I thought I'd simplify things as follows:

f(v::Vector{Tuple}) = v


But I get the same type of error:

ERROR: MethodError: no method matching f(::Array{Tuple{Int64,Int64},1})
Closest candidates are:
f(::Array{Tuple,1}) at none:1
f(::Array{Tuple{Real,Real},1}) at none:1
Stacktrace:
[1] top-level scope at none:1


Is there any way that I can type this in such a way that I can actually call the function with any type of Real elements in the tuples?

Thanks in advance,
Stef

There you go!

function f(v::Array{Tuple{T1,T2},1}) where {T1,T2 <: Real}

I had the same problem yesterday. It turns out that using Real as is does not work because of a concept called ‘invariance’ (see the documentation on Types).

So, what you have to do is indicate the compiler that it can be any type that belongs to Real. That is what the where {T1,T2 <: Real} part means.

4 Likes

Thanks! I’m going to be using this quite a lot I think :slight_smile:

I’d go for f(v::Vector{T}) where T<:Tuple{Real,Real}. That works because tuples are covariant on parameters (i.e. T<:Pair{Real,Real} wouldn’t work the same, and one would need T<:Pair{<:Real, <:Real}).

If both tuple elements are expected to have the same type, f(v::Vector{T}) where {T<:Tuple{R,R}, R<:Real} may be used.

Vector{Tuple} is a concrete type that can hold any tuple, so, from Julia type system point of view, it is different from Vector{Tuple{Int,Int}} which can only hold tuples of two Ints.

Also, I’d recommend using AbstractVector in the signature in case you’d like to pass a vector view at some point in the future.

5 Likes

Note that

f(v::Vector{Tuple{T1,T2}}) where {T1,T2 <: Real} = v

does not restrict the parameter T1, so for example

julia> f([("abc",2)])
1-element Array{Tuple{String,Int64},1}:
 ("abc", 2)

As suggested above, perhaps what you want is

julia> f(v::Vector{<:Tuple{Real,Real}}) = v
f (generic function with 2 methods)

julia> f([(2,1)])
1-element Array{Tuple{Int64,Int64},1}:
 (2, 1)

julia> f([(2.0,1)])
1-element Array{Tuple{Float64,Int64},1}:
 (2.0, 1)
3 Likes