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
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 Int
s.
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