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}
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} .
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: