Why is Matrix{Float64}<:Matrix{Number}
false?
search type invariance. TLDR is that they need different memory representations.
It also follows from the rules of concrete subtyping. Matrix{Number}
is a concrete type that can be instantiated, and concrete types cannot have subtypes.
Try this:
Matrix{Float64} <: Matrix{<:Number}
See also Types · The Julia Language
In particular:
In other words, in the parlance of type theory, Julia’s type parameters are invariant, rather than being covariant (or even contravariant). This is for practical reasons: while any instance of
Point{Float64}
may conceptually be like an instance ofPoint{Real}
as well, the two types have different representations in memory:
- An instance of
Point{Float64}
can be represented compactly and efficiently as an immediate pair of 64-bit values;- An instance of
Point{Real}
must be able to hold any pair of instances ofReal
. Since objects that are instances ofReal
can be of arbitrary size and structure, in practice an instance ofPoint{Real}
must be represented as a pair of pointers to individually allocatedReal
objects.The efficiency gained by being able to store
Point{Float64}
objects with immediate values is magnified enormously in the case of arrays: anArray{Float64}
can be stored as a contiguous memory block of 64-bit floating-point values, whereas anArray{Real}
must be an array of pointers to individually allocatedReal
objects – which may well be boxed 64-bit floating-point values, but also might be arbitrarily large, complex objects, which are declared to be implementations of theReal
abstract type.
Could you elaborate on this a bit more? I am confused by the syntax of Matrix{<:Number}
Invariance of type parameters means that e.g. Matrix{Float64}
is not a subtype of Matrix{Number}
, even if Float64
is a subtype of Number
. However, there exists “wildcard” type parameters in the form of UnionAll
types. Typically such a UnionAll
type is written with the where
clause: Matrix{T} where T<:Number
. It is an abstract type which has all Matrix{T}
as subtypes where T
is a subtype of Number
. A short form is Matrix{<:Number}
, which can be used if you do not need to use the type variable T
elsewhere.
julia> dump(Matrix{<:Number})
UnionAll
var: TypeVar
name: Symbol #s3
lb: Union{}
ub: Number <: Any
body: Array{var"#s3"<:Number, 2} <: DenseArray{var"#s3"<:Number, 2}
julia> dump(Matrix{T} where T<:Number)
UnionAll
var: TypeVar
name: Symbol T
lb: Union{}
ub: Number <: Any
body: Array{T<:Number, 2} <: DenseArray{T<:Number, 2}
Beat me to it
This was going to be my response:
As others have mentioned, Matrix{Number}
is a concrete type, and one concrete type cannot subtype another concrete type.
Matrix{<:Number}
is a union of types, and is shorthand for Matrix{T} where {T<:Number}
.