# Why \[1, 2, 3\] is not a Vector{Number}?

**URL:** <https://discourse.julialang.org/t/why-1-2-3-is-not-a-vector-number/52645>\
**Category:** New to Julia\
**Tags:** question, parametric-types\
**Created:** [December 31, 2020, 8:26am UTC](https://discourse.julialang.org/t/why-1-2-3-is-not-a-vector-number/52645 "2020-12-31T08:26:16Z")\
**Posts on this page:** 20\
**Page:** 1

<div class="post-metadata">

**Author:** ![Bryan\_So](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/bryan_so/32/16228_2.png) [@Bryan\_So](https://discourse.julialang.org/u/Bryan_So)\
**Post date:** [December 31, 2020, 8:26am UTC](https://discourse.julialang.org/t/why-1-2-3-is-not-a-vector-number/52645/1 "2020-12-31T08:26:16Z")

</div>

I don’t understand this:

```julia
julia> isa([1,2,3], Vector{Number})
false

```

Each 1, 2, or 3 is certainly a Number:

```julia
julia> isa(1, Number)
true

```

It is illogical to say [1, 2, 3] is not Vector{Number} when each element is a Number.

---

<div class="post-metadata">

**Author:** ![Keno](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/keno/32/285_2.png) [@Keno](https://discourse.julialang.org/u/Keno)\
**Post date:** [December 31, 2020, 8:28am UTC](https://discourse.julialang.org/t/why-1-2-3-is-not-a-vector-number/52645/2 "2020-12-31T08:28:26Z")

</div>

See the relevant manual section: [Types · The Julia Language](https://docs.julialang.org/en/v1/manual/types/#Parametric-Composite-Types)

---

<div class="post-metadata">

**Author:** ![jules](https://avatars.discourse-cdn.com/v4/letter/j/41988e/32.png) [@jules](https://discourse.julialang.org/u/jules)\
**Post date:** [December 31, 2020, 8:56am UTC](https://discourse.julialang.org/t/why-1-2-3-is-not-a-vector-number/52645/3 "2020-12-31T08:56:09Z")

</div>

Just to extract some relevant points because the manual is quite dense:

`Vector{Number}` is a concrete type, and `[1, 2, 3]` has type `Vector{Int}`, which is also a concrete type. One concrete type is never a subtype of another concrete type, they are the leaves of the type tree.

`Vector{Number}` is concrete, even though `Number` is not a concrete type. That’s because it has a concrete implementation which can store all types that are subtypes of `Number`, it has a specific memory layout etc. On the other hand, `AbstractVector{Int}` is the other way around and not a concrete type, because the container is abstract even though the element is concrete.

What you can do instead is `[1, 2, 3] isa Vector{<:Number}` which is `true`. That’s because `<:Number` is a sort of placeholder which means “any type which is a subtype of Number”. This is often needed for dispatching on containers where you want to allow set of element types. `f(x::Vector{Number})` can take only arguments of type `Vector{Number}`, whereas `g(x::Vector{<:Number})` can take e.g. `Vector{Int}`, `Vector{Float64}`, `Vector{Real}`, `Vector{Number}`, etc.

---

<div class="post-metadata">

**Author:** ![Bryan\_So](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/bryan_so/32/16228_2.png) [@Bryan\_So](https://discourse.julialang.org/u/Bryan_So)\
**Post date:** [December 31, 2020, 9:11am UTC](https://discourse.julialang.org/t/why-1-2-3-is-not-a-vector-number/52645/4 "2020-12-31T09:11:22Z")

</div>

Wow. Thanks Jules. This is a good summary. I wasn’t able to make any sense of the reference manual, frankly speaking (besides yep it is what it is).

---

<div class="post-metadata">

**Author:** ![sairus7](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/sairus7/32/10816_2.png) [@sairus7](https://discourse.julialang.org/u/sairus7)\
**Post date:** [December 31, 2020, 3:25pm UTC](https://discourse.julialang.org/t/why-1-2-3-is-not-a-vector-number/52645/5 "2020-12-31T15:25:17Z")

</div>

`isa([1,2,3], Vector{T} where T<:Number)` is also `true`.

There is also a nice video about subtyping: [JuliaCon 2018 | Subtyping made friendly | Francesco Zappa Nardelli - YouTube](https://youtu.be/Y95fAipREHQ?t=958)

---

<div class="post-metadata">

**Author:** ![aplavin](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/aplavin/32/222056_2.png) [@aplavin](https://discourse.julialang.org/u/aplavin)\
**Post date:** [December 31, 2020, 3:46pm UTC](https://discourse.julialang.org/t/why-1-2-3-is-not-a-vector-number/52645/6 "2020-12-31T15:46:33Z")

</div>

There is a very simple explanation why you actually wouldn’t want `isa([1,2,3], Vector{Number})` to be `true`. `[1,2,3]` just cannot generally be used where a `Vector{Number}` is expected. Suppose you have a function:

```julia
f(vec::Vector{Number}) = push!(vec, 1.5)

```

It works with `Vector{Number}`:

```julia
vn = Vector{Number}([1, 2, 3])
f(vn)

```

But it would fail if one could pass `Vector{Int}` to it:

```julia
g(vec) = push!(vec, 1.5)

vi = [1, 2, 3]
g(vi) # error - cannot push 1.5 to vector of ints

```

---

<div class="post-metadata">

**Author:** ![rafael.guerra](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/rafael.guerra/32/216610_2.png) [@rafael.guerra](https://discourse.julialang.org/u/rafael.guerra)\
**Post date:** [December 31, 2020, 4:38pm UTC](https://discourse.julialang.org/t/why-1-2-3-is-not-a-vector-number/52645/7 "2020-12-31T16:38:56Z")

</div>

@sairus7, thanks, really nice video except for the sound quality (had to use a sound booster extension to Chrome). It seems that Francesco Nardelli’s talk covers the material in this [paper](https://fzn.fr/projects/lambdajulia/paper.pdf).

---

<div class="post-metadata">

**Author:** ![Bryan\_So](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/bryan_so/32/16228_2.png) [@Bryan\_So](https://discourse.julialang.org/u/Bryan_So)\
**Post date:** [December 31, 2020, 10:44pm UTC](https://discourse.julialang.org/t/why-1-2-3-is-not-a-vector-number/52645/8 "2020-12-31T22:44:36Z")

</div>

All these explanations fall into “it is what it is”. Mr. Nardelli explained in the video “It is a language design choice”.

Which is OK. I suspect it’s for performance in dispatching. Will a Vulcan agree it is the logical thing to define? Probably not.

`A = [1, 2, 3]`

Is A a vector? Yes.

> julia\> isa(A, Vector)  
> true

Are all elements of A Numbers? Yes.

```julia
julia> all(x -> isa(x, Number), A)
true

```

Yet, A is not a Vector{Number}

```julia
julia> isa(A, Vector{Number})
false

```

As I said, I really don’t have a problem remembering the fact A is not a Vector{Number} and remember to use Vector{\<:Number} to get what I want. So no offense. Just a discussion of programming design and general logic thinking. Sometimes if we identify a language design choice is not natural, then perhaps many unintentional bugs may arise from it.

Digression: Reminds me of a Chinese philosophy: [White Horse is Not a Horse](https://en.wikipedia.org/wiki/When_a_white_horse_is_not_a_horse)

Thanks.

---

<div class="post-metadata">

**Author:** ![Bryan\_So](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/bryan_so/32/16228_2.png) [@Bryan\_So](https://discourse.julialang.org/u/Bryan_So)\
**Post date:** [December 31, 2020, 10:55pm UTC](https://discourse.julialang.org/t/why-1-2-3-is-not-a-vector-number/52645/9 "2020-12-31T22:55:55Z")

</div>

apalvin:

Thanks.

Slightly different from what I had in mind. Your example is destructive. An auto promotion of some kind. Not what I’m expecting. Good example.

The original question is a simple non-destructive subsumption relationship definition question.

It’s perfectly feasible to design the language to not allow auto promotion but allows the other case.

Thx

---

<div class="post-metadata">

**Author:** ![Bryan\_So](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/bryan_so/32/16228_2.png) [@Bryan\_So](https://discourse.julialang.org/u/Bryan_So)\
**Post date:** [January 1, 2021, 12:23am UTC](https://discourse.julialang.org/t/why-1-2-3-is-not-a-vector-number/52645/10 "2021-01-01T00:23:04Z")

</div>

Forgot to…

Wish you all happy new year.

---

<div class="post-metadata">

**Author:** ![Tamas\_Papp](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/tamas_papp/32/25949_2.png) [@Tamas\_Papp](https://discourse.julialang.org/u/Tamas_Papp)\
**Post date:** [January 1, 2021, 9:07am UTC](https://discourse.julialang.org/t/why-1-2-3-is-not-a-vector-number/52645/11 "2021-01-01T09:07:06Z")

</div>

> [@Bryan\_So](#):
>
> Will a Vulcan agree it is the logical thing to define? Probably not.

Generally it is not very useful to consider programming language design decisions from the perspective of being “logical” — you can’t really derive them from basic principles of logic, so these criteria usually just reflect user expectations. But those are very heterogeneous, and depend on your prior experience and what languages you were exposed to, among other things.

The important thing is that once you are aware of how this works in Julia, it is easy to remember and applies consistently through the type system.

---

<div class="post-metadata">

**Author:** ![aplavin](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/aplavin/32/222056_2.png) [@aplavin](https://discourse.julialang.org/u/aplavin)\
**Post date:** [January 1, 2021, 12:29pm UTC](https://discourse.julialang.org/t/why-1-2-3-is-not-a-vector-number/52645/12 "2021-01-01T12:29:12Z")

</div>

> [@Bryan\_So](#):
>
> Your example is destructive. An auto promotion of some kind. Not what I’m expecting. Good example.

There is no type-promotion in my example, as far as I understand. It just shows a function that works for `Vector{Number}` but fails for `Vector{Int}`. This means one cannot substitute the latter for the former.

---

<div class="post-metadata">

**Author:** ![Vasily\_Pisarev](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/vasily_pisarev/32/7929_2.png) [@Vasily\_Pisarev](https://discourse.julialang.org/u/Vasily_Pisarev)\
**Post date:** [January 1, 2021, 6:23pm UTC](https://discourse.julialang.org/t/why-1-2-3-is-not-a-vector-number/52645/13 "2021-01-01T18:23:02Z")

</div>

> [@Bryan\_So](#):
>
> All these explanations fall into “it is what it is”. Mr. Nardelli explained in the video “It is a language design choice”.

It’s not just arbitrary choice though.

An object can be covariant only on types it “produces” and contravariant only on types it “consumes”.

E.g., a function is covariant on its return type (i.e., we can use a function returning an `Int` anywhere we expect a function returning a `Number`) and contravariant on argument type (i.e., we can use a function accepting a `Number` anywhere we expect a function accepting an `Int`).

The problem with `Vector` specifically (and other mutable containers), as pointed out by @aplavin, is that it can be used both as producer and consumer. So, the answer to `[1, 2, 3] isa Vector{Number}` should depend on whether you want to read from or write to the vector (not necessarily `push!`, just an assignment of a vector element raises the same issue) if we want co- or contravariant parameters. In Julia, it happens that type parameter invariance improves performance, but a programmer can emulate co-/contravariance if needed by explicitly using `<:` or `>:`.

---

<div class="post-metadata">

**Author:** ![Mattriks](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/mattriks/32/351_2.png) [@Mattriks](https://discourse.julialang.org/u/Mattriks)\
**Post date:** [January 1, 2021, 7:57pm UTC](https://discourse.julialang.org/t/why-1-2-3-is-not-a-vector-number/52645/14 "2021-01-01T19:57:04Z")

</div>

Julia’s subtyping is based on [Set logic](https://docs.julialang.org/en/v1/devdocs/types/). In set logic it’s definitely possible that  
`isa([white horse], Vector{<:Horse}) == true` but  
`isa([white horse], Vector{Horse}) == false`.

---

<div class="post-metadata">

**Author:** ![CameronBieganek](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/cameronbieganek/32/6915_2.png) [@CameronBieganek](https://discourse.julialang.org/u/CameronBieganek)\
**Post date:** [January 2, 2021, 10:32pm UTC](https://discourse.julialang.org/t/why-1-2-3-is-not-a-vector-number/52645/15 "2021-01-02T22:32:37Z")

</div>

The concept of subtyping is often defined via the [Liskov substitution principle](https://en.wikipedia.org/wiki/Liskov_substitution_principle). Quoting from Wikipedia:

> Let \phi (x) be a property provable about objects x of type T. Then \phi (y) should be true for objects y of type S where S is a subtype of T.

Speaking in looser terms, we can phrase the substitution principle as follows:

- If S is a subtype of T, then objects of type S can be used anywhere an object of type T is required.

A few more definitions that are relevant (from a language agnostic point of view):

- A [_type constructor_](https://en.wikipedia.org/wiki/Type_constructor) is an operator that takes 0 or more types and returns a new type. For example, `Vector` is a type constructor that takes a type `T` and creates a new type `Vector{T}`.
- A type constructor is either _covariant_, _contravariant_, _bivariant_, or _invariant_ in each of its input type variables. ([Wikipedia reference.](https://en.wikipedia.org/wiki/Covariance_and_contravariance_%28computer_science%29))
- If `I` is a type constructor, `B <: A`, and `I{B} <: I{A}`, then `I` is _covariant_.
- If `I` is a type constructor, `B <: A`, and `I{A} <: I{B}`, then `I` is _contravariant_.
- If `I` is not covariant or contravariant (or bivariant), then `I` is _invariant_. In other words, `B <: A` does not imply any subtype relationship for `I{B}` and `I{A}`.

So, if `Cat <: Animal`, is `Vector{Cat} <: Vector{Animal}`? Consider the following two properties of a `Vector{Animal}`:

1. Using `getindex` to read an element from the vector returns an `Animal`.
2. `setindex!` can be used to set the value on an element of the vector to any `Animal`, for instance a `Dog`.

A `Vector{Cat}` satisfies the first property—reading an element of a `Vector{Cat}` returns a `Cat`, which is an `Animal`. However, a `Vector{Cat}` does not satisfy the second property—you can’t set an element of a `Vector{Cat}` to a value with type `Dog`. Thus, by the substitution principle, `Vector{Cat}` cannot be a subtype of `Vector{Animal}`. So the `Vector` type constructor needs to be invariant.

Notice that the issue arises because of property #2, in other words, because vectors are mutable. If vectors were immutable, then it would be reasonable to have `Vector{Cat} <: Vector{Animal}`, i.e., for `Vector` to be covariant. In fact, tuples are immutable and are covariant:

```julia
julia> Int <: Number
true

julia> Tuple{Int} <: Tuple{Number}
true

```

However, it’s not quite accurate to use the above argument to explain why `Vector{T}` is invariant in `T`. The real reason is because _all_ parametric types in Julia are invariant (except for tuples). `Vector{T}` just happens to be one case of a parametric type. To borrow an example from the manual, we could define an immutable `Point` type, like so:

```julia
struct Point{T}
    x::T
    y::T
end

```

The `Point` type is immutable, so one could legitimately ask why `Point{Float64}` is not a subtype of `Point{Real}`. In other words, why isn’t `Point{T}` covariant in `T`? The manual does provide [some explanation](https://docs.julialang.org/en/v1/manual/types/#Parametric-Composite-Types):

> 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:
> 
> - 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 of `Real`. Since objects that are instances of `Real` can be of arbitrary size and structure, in practice an instance of `Point{Real}` must be represented as a pair of pointers to individually allocated `Real` objects.

So, if I interpret that correctly, that means that the main reason that parametric types are invariant in Julia is due to performance considerations. However, there could be more to the story—I’d be interested to hear a more detailed explanation of the design decision.

Languages like C#, Scala, and OCaml have a syntax to declare whether custom types are covariant, contravariant, or invariant. For example, Scala prefixes type variables with `+` or `-` to indicate covariant or contravariant, respectively. If Julia had syntax like that, then we could do something like this:

```julia
struct ImmutableVector{+T,N}
    x
end

```

and then we would have `ImmutableVector{Float64} <: ImmutableVector{Real}`.

---

<div class="post-metadata">

**Author:** ![stevengj](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/stevengj/32/71_2.png) [@stevengj](https://discourse.julialang.org/u/stevengj)\
**Post date:** [January 2, 2021, 10:55pm UTC](https://discourse.julialang.org/t/why-1-2-3-is-not-a-vector-number/52645/16 "2021-01-02T22:55:06Z")

</div>

> [@CameronBieganek](#):
>
> that means that the main reason that parametric types are invariant in Julia is due to performance considerations

Yes, it’s critical for performance. See my explanation here: [Problem with Complex{Rationals} - #7 by stevengj](https://discourse.julialang.org/t/problem-with-complex-rationals/9474/7)

---

<div class="post-metadata">

**Author:** ![greg\_plowman](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/greg_plowman/32/8100_2.png) [@greg\_plowman](https://discourse.julialang.org/u/greg_plowman)\
**Post date:** [January 3, 2021, 1:23am UTC](https://discourse.julialang.org/t/why-1-2-3-is-not-a-vector-number/52645/17 "2021-01-03T01:23:53Z")

</div>

> [@stevengj](#):
>
> Yes, it’s critical for performance. See my explanation here: [Problem with Complex{Rationals} - #7 by stevengj](https://discourse.julialang.org/t/problem-with-complex-rationals/9474/7)

Whilst I understand the performance differences, I don’t fully get this:

> If you have a compiled function that operates on an Complex{Real} or an array of Complex{Real}, therefore, it is completely different from code that operates on Complex{Rational{Int}}. You can’t use either one for the other. (If A is a subtype of B, it essentially means “compiled code for B can be used on instances of A” — this is the purpose of the subtype relation, to decide what code is applicable to what types.)

Doesn’t Julia specialize implementations of function methods for each concrete argument type?  
But Julia doesn’t allow subtyping from concrete types. Presumably there are good reasons for this. (naively, it seems it would create complications).  
Subtype relation is used for dispatch, not compiled code?

Conceptually, if Julia allowed subtyping from concrete types, would there be a performance issue? Would the specialization handle this correctly?

So maybe the reason for invariant parametric types is a bit more complicated?

---

<div class="post-metadata">

**Author:** ![Tamas\_Papp](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/tamas_papp/32/25949_2.png) [@Tamas\_Papp](https://discourse.julialang.org/u/Tamas_Papp)\
**Post date:** [January 3, 2021, 8:06am UTC](https://discourse.julialang.org/t/why-1-2-3-is-not-a-vector-number/52645/18 "2021-01-03T08:06:12Z")

</div>

> [@greg\_plowman](#):
>
> if Julia allowed subtyping from concrete types

I am not sure what that would look like.

---

<div class="post-metadata">

**Author:** ![stevengj](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/stevengj/32/71_2.png) [@stevengj](https://discourse.julialang.org/u/stevengj)\
**Post date:** [January 3, 2021, 2:02pm UTC](https://discourse.julialang.org/t/why-1-2-3-is-not-a-vector-number/52645/19 "2021-01-03T14:02:36Z")

</div>

> [@greg\_plowman](#):
>
> But Julia doesn’t allow subtyping from concrete types. Presumably there are good reasons for this.

Imagine what this would mean for working with an array `a` of type `Vector{Float64}`, for example:

- Currently, this can be stored as a consecutive sequence of 8-byte chunks in memory, and if the compiler sees `a[i] + 1.5` it knows that it can compile this into a load instruction and a floating-point addition.

- If `Float64` could be sub-typed, however, then the elements of `a` could be any subtypes of `Float64` — they would have to be pointers to “boxes” containing a type tag (to say what type they really are) along with the actual data, and for an expression like `a[i]+1.5` the compiler would need to generate code that chases the pointer, looks at the type tag, and does dynamic (runtime) dispatch to the correct `+` operation.

Moreover, dynamic dispatch in a multiple-dispatch language like Julia is even more expensive than dynamic dispatch in a single-dispatch (traditional OOP) language like C++ (where they have vtables), so **devirtualization** (figuring what what methods to call at compile-time) is a critical optimization, to the point where nearly all optimized Julia code is devirtualized in practice. Making concrete types final (non-subtypable) gives the compiler many more chances for devirtualization. Even in C++, [people often recommend `final` classes](https://devblogs.microsoft.com/cppblog/the-performance-benefits-of-final-classes/) to enable devirtualization.

Because multiple dispatch allows you to add new functions to existing types (see also [this discussion](https://discourse.julialang.org/t/is-julias-way-of-oop-superior-to-c-python-why-julia-doesnt-use-class-based-oop/52058/24) and [this talk](https://www.youtube.com/watch?v=kc9HwsxE1OY) on the [expression problem](https://en.wikipedia.org/wiki/Expression_problem) of OOP), there is much less need for subtyping of concrete types in Julia than there is in OOP languages. Because of that, it was a practical choice to make all concrete types “final”, reaping vast benefits in performance as well as simplifying the language.

---

<div class="post-metadata">

**Author:** ![mkitti](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/mkitti/32/12459_2.png) [@mkitti](https://discourse.julialang.org/u/mkitti)\
**Post date:** [January 3, 2021, 2:08pm UTC](https://discourse.julialang.org/t/why-1-2-3-is-not-a-vector-number/52645/20 "2021-01-03T14:08:01Z")

</div>

I think the boolean question you are trying to ask is actually this:

```julia
julia> typeof([1,2,3]) <: Vector{T} where T <: Number
true

```

or maybe simpler:

```julia
julia> isa([1,2,3], Vector{<:Number})
true

```

Also see this:

```julia
julia> isa(Number[1,2,3], Vector{Number})
true

```

[Next page](https://discourse.julialang.org/t/why-1-2-3-is-not-a-vector-number/52645.md?page=2)
