3
votes

I have a 1-dimensional Array of Tuples which I need to pass to a function defined as

function f(a::Array{Tuple{Vararg{String}}, 1}) 
    #do some processing
end

Each Tuple can have any number of String elements, but the number of elements will be the same for all the Tuples in the Array. For example, the Array could look like [("x1","x2"),("y1","y2")] or [("x1","x2","x3"),("y1","y2","y3")] etc. Hence the usage of Vararg{String} for this.

Now, when I run f([("x1","x2"),("y1","y2")]), it throws an error

"MethodError: no method matching f(::Array{Tuple{String,String},1})"

How should I modify the function definition so that this works?

2
You could just leave out the type description from the function call. - niczky12
@niczky12 I need to ensure that the caller sends Tuples with only String elements for my use case. - WebDev

2 Answers

6
votes

You get this MethodError because Tuple{Vararg{T}} is not a concrete type as the number of elements for Vararg are left unspecified and Julia's type parameters are invariant rather than covariant.

Even though we have Tuple{Vararg{String, 5}} <: Tuple{Vararg{String}}, we do not have Vector{Tuple{Vararg{String, 5}}} <: Vector{Tuple{Vararg{String}}}

julia> Tuple{Vararg{String, 5}} <: Tuple{Vararg{String}}
true

julia> Vector{Tuple{Vararg{String, 5}}} <: Vector{Tuple{Vararg{String}}}
false

You should instead use the following signatures to get rid of the error

function f(a::Vector{<:Tuple{Vararg{String}}})
# or
function f(a::Vector{T}) where {T <: Tuple{Vararg{String}}

as suggested by both @crstnbr and @BogumiłKamiński. These signatures will get rid of the error, however, they do not restrict Tuples to be of the same length. For example, you can call these functions with

f([("x1, x2"), ("y1", "y2", "y3"])

As you want to ensure the tuples in the array contain the same number of elements, you need to specify this restriction in the type annotation. The documentation for Vararg provides the information on how to specify the number of elements.

Vararg{T,N}

The last parameter of a tuple type Tuple can be the special type Vararg, which denotes any number of trailing elements. The type Vararg{T,N} corresponds to exactly N elements of type T. Vararg{T} corresponds to zero or more elements of type T. Vararg tuple types are used to represent the arguments accepted by varargs methods (see the section on Varargs Functions in the manual.)

You can go with

function f(a::Vector{Tuple{Vararg{String, N}}}) where N 
    ...
end

or using the compact way of NTuple instead

function f(a::Vector{NTuple{N, String}}) where N

end

These signatures will enforce the restrictions you wanted in the question.


Notes

You might be overly specializing your types.

As @Bogumił Kamiński noted in the comments section, it is better to work with AbstractString type instead of the concrete String type.

function f(a::Vector{<:NTuple{N, AbstractString}}) where N
...
end

You may consider working with AbstractVector instead of Vector, as well.

1
votes

You can modify the signature to

function f(a::Array{<:Tuple{Vararg{String}}, 1})
    #do some processing
end

Note the <: to indicate that all subtypes of Tuple{Vararg{String}} are fine.

However, it might be easier to just leave out the type information. The function will have exactly the same speed.