Method error with Pair including abstract type

My expectations seem to be wrong about how defining a method for a Pair that includes an abstract type works. I want to be able to pass a Pair that comprises a Symbol and a Number, but without being specific about Int, Float, etc. I have the following sample code:

println(":x is a Symbol: ", isa(:x, Symbol))
println("1 is a Number: ", isa(1, Number))
foo(a::Pair{Symbol, Number}) = println(a)

foo(:x => 1)

which produces output like:

✗1 % julia reference/dispatch-example.jl
:x is a Symbol: true
1 is a Number: true
ERROR: LoadError: MethodError: no method matching foo(::Pair{Symbol, Int64})
Closest candidates are:
  foo(::Pair{Symbol, Number}) at dispatch-example.jl:3
Stacktrace:
 [1] top-level scope
   @ dispatch-example.jl:5
in expression starting at dispatch-example.jl:5

I don’t understand why my Pair{Symbol, Int64} doesn’t match Pair{Symbol, Number}

Here’s one way to make it work:

julia> foo(a::Pair{Symbol, T} where T<:Number) = println(a)
foo (generic function with 1 method)

julia> foo(:x => 1)
:x => 1

julia>
3 Likes

This is a general property of all parametric types in Julia (except Tuple, which is its own thing), called invariance. You can read all about it here: Types · The Julia Language In particular, it talks about a similar case:

This last point is very important: even though Float64 <: Real we DO NOT have Point{Float64} <: Point{Real} .

3 Likes

Maybe this helps: Vector{Int} <: Vector{Real} is false??? · JuliaNotes.jl

The argument there is about vectors, but the same holds for pairs.

3 Likes

I don’t think I realized that I had ventured into parametric type territory! Thanks so much for the solution and the explanations.

1 Like

Yup, Julia is relatively unusual in that things like Pair are not some special built-in object but just another regular struct like any other you might create yourself. You can see the entire definition of Pair here:

2 Likes