Functions of tuples of matrices VS Functions of vectors of tuples of matrices

I get a MethodError in my code and I’m trying to understand the behaviour. Functions of tuples of matrices work and functions of vectors of tuples of matrices give a MethodError. A minimum working example would be the following:

julia> A=([[1,2] [3,4]], [[5,6] [7,8]])
([1 3; 2 4], [5 7; 6 8])


julia> function f(P::Tuple{Matrix, Matrix}) return P[1]+P[2] end
f (generic function with 1 method)

julia> f(A)
2×2 Matrix{Int64}:
 6  10
 8  12

julia> function f(P::Vector{Tuple{Matrix, Matrix}}) return [Q[1]+Q[2] for Q in P] end
f (generic function with 2 methods)

julia> f([A])
ERROR: MethodError: no method matching f(::Vector{Tuple{Matrix{Int64}, Matrix{Int64}}})

Closest candidates are:
  f(::Tuple{Matrix, Matrix})
   @ Main REPL[3]:1
  f(::Vector{Tuple{Matrix, Matrix}})
   @ Main REPL[5]:1

Stacktrace:
 [1] top-level scope
   @ REPL[6]:1

I fixed this as follows:

julia> function g(P::Vector{Tuple{Matrix{T}, Matrix{T}}}) where T return [Q[1]+Q[2] for Q in P] end
g (generic function with 1 method)

julia> g([A])
1-element Vector{Matrix{Int64}}:
 [6 10; 8 12]

To avoid getting this error in the future, my question is why the first thing doesn’t work and the second thing does and in what cases I will encounter this.

The first thing works, because Tuple is a bit of a special type. In particular, its parameters are not invariant (see here for more explanations: Types · The Julia Language ).

For any parametric type T that is not Tuple (or Union – I think these two are the only exceptions?), this is the behavior:

julia> Int <: Integer
true

julia> Array{Int} <: Array{Integer}
false

But for Tuple, we have

julia> Tuple{Int} <: Tuple{Integer}
true

Since the type of your variable A is

julia> typeof(A)
Tuple{Matrix{Int64}, Matrix{Int64}}

the concrete tuple type is a subtypes of the abstract one, but wrapped in the Array or any other type, that breaks:

julia> typeof(A) <: Tuple{Matrix, Matrix}
true

julia> Array{typeof(A)} <: Array{Tuple{Matrix, Matrix}}
false

Long story short: That it doesn’t work is the default – Tuple is the exception. If you find that it’s often too verbose to annotate the type parameters like in your example, you could try to just remove the argument type annotation (if you don’t need it for dispatch). If it’s not clear what the function argument needs to be then, perhaps a custom struct might help.

2 Likes