My apologies if this has been covered previously. I just got burned by this, I was assuming I could use ::Vector{Any} to cover an input of Vector{Float64}, Vector{Int}, etc. But it turns out that Julia doesn’t believe Vector{Float64} is a subtype of Vector{Any}, I need to use instead…
This last point is very important: even though Float64 <: Real we DO NOT have Point{Float64} <: Point{Real} .
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 of Point{Real} as well, the two types have different representations in memory:
This appears to be one of the most frequently asked questions for beginners posted in this forum? I have encountered two similar posts in two days. Do we have a FAQ section or a MUST READ page for beginners in the Julia doc for all these Julia-specific niche issues?
This might be mainly for practical reasons, but also note that there is actually such a thing as a concrete Vector{Any}. Let’s say you have a function that looks like this:
function foo(x::Vector{Any})
push!(x, 5)
push!(x, "Hello")
return x
end
What helps me understand better is the parametric notation…
f(x::Vector{T}) where {T === Any} = println("Vector{T} where T is only ::Any i.e. Vector{Any}")
f(x::Vector{T}) where {T <: Any} = println("Vector{T} where T can be anything")
f(x::Vector{T}) where {T === Float64} = println("Vector{T} where T is only ::Float64 i.e. Vector{Float64}")
One problem though is T === Any doesn’t work. Does anyone know how to properly write this in Julia? I feel like this notation may be a good best practice to avoid confusion.
f(x::Vector{Any}) = "Vector{T} where T is only Any"
f(x::Vector{<:Any}) = "Vector{T} where T can be anything"
f(x::Vector{Float64}) = "Vector{T} where T is only Float64"
In action:
julia> f(Any[1])
"Vector{T} where T is only Any"
julia> f(Int[1])
"Vector{T} where T can be anything"
julia> f(Float64[1])
"Vector{T} where T is only Float64"
The construct where {T === X} isn’t supported because it doesn’t increase expressiveness: you can just eliminate the type parameter T entirely by substituting its value everywhere T appears. In other words, where clauses are only useful with upper/lower bounds. You can, however, enforce equality by using an upper and lower bound that are the same, so this does what you want using where clauses:
f(x::Vector{T}) where {Any <: T <: Any} = "Vector{T} where T is only Any"
f(x::Vector{T}) where {T <: Any} = "Vector{T} where T can be anything"
f(x::Vector{T}) where {Float64 <: T <: Float64} = "Vector{T} where T is only Float64"