I am currently writing a library that for a number of applications needs to take a dictionary of matrices as arguments. In order to make the functions reasonably general, I wanted to make the key/value types in the function arguments abstract. e.g. something like:
function print_dict(dict::AbstractDict{<:Real, AbstractArray{<:Real, 2}})
print(dict)
end
(This is not a real function I want to write, just for demonstration of the argument type)
Calling this function with a dict of what I assumed to be a valid subtype of the function argument
ERROR: LoadError: MethodError: no method matching print_dict(::Dict{Int64,Array{Float32,2}})
Closest candidates are:
print_dict(::AbstractDict{var"#s1",AbstractArray{var"#s2",2} where var"#s2"<:Real} where var"#s1"<:Real) at ***/dict_weirdness.jl:1
Stacktrace:
[1] top-level scope at ***/dict_weirdness.jl:10
[2] include(::String) at ./client.jl:457
[3] top-level scope at REPL[1]:1
in expression starting at ***/dict_weirdness.jl:10
I expected this to work as you can do things like AbstractArray{<:Real, 2} as functional argument types no problem.
Can anyone explain why this does not work, and perhaps suggest any workarounds / better practises?
Thank you both, this is enlightening. For some reason, I had not expected the need of <: before the AbstractArray. Typically you don’t need this when the function argument is of AbstractArray type, e.g.
But you do have the additional <: in your last example – it’s the one in front of the Real. This is really the point made in the docs in a warning box:
This last point is very important: even though Float64 <: Real we DO NOT have Point{Float64} <: Point{Real} .
If you have a parametric type Foo{T} and types A <: B, then it is not true that Foo{A} <: Foo{B}. It is, however, true that:
Foo{A} <: (Foo{X} where X <: B)
and, equivalently, that:
Foo{A} <: Foo{<:B}
In both cases you are saying "Foo{A} is a subtype of the collection of all types Foo{X} where X is some subtype of B".
In your case, Array{Float64, 2} <: AbstractArray{<:Real, 2}, but that does not mean that Dict{Int, Array{Float64, 2}} <: Dict{Int, AbstractArray{<:Real, 2}}.
Instead, you can say:
Dict{Int, Array{Float64, 2}} <: (Dict{Int, A} where A <: AbstractArray{<:Real, 2})
which means, roughly, "Dict{Int, Array{Float64, 2}} is a member of the set of all types Dict{Int, A} in which A is some kind of AbstractArray{R, 2} and R is some kind of Real.