# `Vector{Float64} <: Vector{Any}` = false, why?

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…

``````Vector{Float64} <: Any
``````

Or it also seems that this works too…

``````Vector{Float64} <: Vector
``````

What am I missing?

1 Like

Running the code below in the REPL gives true.

``````julia> Vector{Float64} <: Vector{<:Any}
true
``````

If you want to check if a type is subtype of a `Vector` of `Int`s or `Float64`s, you can do something like:

``````julia> Vector{Float64} <: Vector{<:Union{Float64, Int}}
true
``````

Or better yet:

``````julia> Vector{Float64} <: Vector{<:Real}
true
``````

I’m not sure how that works though

3 Likes

This is what was throwing me for a loop…

``````function test(x::Vector{Any})
println(length(x))
end

test(zeros(10))
``````

which gives the error:

``````ERROR: MethodError: no method matching test(::Vector{Float64})
Closest candidates are:
test(::Vector{Any})
``````

It turns out the notation you used solves the problem…

``````function test(x::Vector{<:Any})
println(length(x))
end
``````

Looks like I learned something new, didn’t realize I could use `<:` in the type definition.

Thanks!

2 Likes

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:

https://docs.julialang.org/en/v1/manual/types/#Parametric-Types

11 Likes

This is covered in the Inheritance section of this (almost ready) course.
In brief, use templates in these situations.

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?

3 Likes

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
``````

Then

``````julia> foo(Any[])
2-element Vector{Any}:
5
"Hello"
``````

What should happen if you pass in a `Vector{Float64}`?

1 Like

Another example

``````julia> [1, 1.0, :a]
3-element Vector{Any}:
1
1.0
:a

julia> foo(x::Vector{Any}) = length(x)
foo (generic function with 1 method)

julia> foo([1, 1.0, :a])
3

julia> foo([1, 2, 3])
ERROR: MethodError: no method matching foo(::Vector{Int64})
Closest candidates are:
foo(::Vector{Any}) at REPL:1
Stacktrace:
 top-level scope
@ REPL:1
``````

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.

3 Likes

You can write all of those without where clauses:

``````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)
"Vector{T} where T is only Any"

julia> f(Int)
"Vector{T} where T can be anything"

julia> f(Float64)
"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"
``````
11 Likes

My take on that: Vector{Int} <: Vector{Real} is false??? · JuliaNotes.jl

See Why doesn’t it work to declare `foo(bar::Vector{Real}) = 42` and then call `foo()`? in the Julia FAQ.

4 Likes

Another thread on why parametric types are invariant: Reason behind designing parametric types as invariant. Note that these covariance and contravariace relations hold:

• covariance: `S <: T``P{<:S} <: P{<:T}`
• invariance: `S ≠ T` ⇒ ¬ `P{S} <: P{T}`
• contravariance: `S <: T``P{>:S} >: P{>:T}`
1 Like