# Simple question about Julia types

**URL:** <https://discourse.julialang.org/t/simple-question-about-julia-types/100077>\
**Category:** New to Julia\
**Tags:** type\
**Created:** [June 9, 2023, 12:31am UTC](https://discourse.julialang.org/t/simple-question-about-julia-types/100077 "2023-06-09T00:31:11Z")\
**Posts on this page:** 12\
**Page:** 1

<div class="post-metadata">

**Author:** ![mmv](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/mmv/32/26323_2.png) [@mmv](https://discourse.julialang.org/u/mmv)\
**Post date:** [June 9, 2023, 12:31am UTC](https://discourse.julialang.org/t/simple-question-about-julia-types/100077/1 "2023-06-09T00:31:12Z")

</div>

Hi!

Given the following statements are true:

```julia
julia> Matrix <: AbstractMatrix
true

julia> Float64 <: Number
true

```

why is it that

```julia
julia> Matrix{Float64} <: AbstractMatrix{Number}
false

```

?

---

<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:** [June 9, 2023, 12:33am UTC](https://discourse.julialang.org/t/simple-question-about-julia-types/100077/2 "2023-06-09T00:33:31Z")

</div>

`Float64` is not `Number`. It’s a subtype of `Number`.

```julia
julia> Matrix{Float64} <: AbstractMatrix{Float64}
true

julia> Matrix{Float64} <: AbstractMatrix{<:Number}
true

```

---

<div class="post-metadata">

**Author:** ![mmv](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/mmv/32/26323_2.png) [@mmv](https://discourse.julialang.org/u/mmv)\
**Post date:** [June 9, 2023, 12:39am UTC](https://discourse.julialang.org/t/simple-question-about-julia-types/100077/3 "2023-06-09T00:39:02Z")

</div>

Thanks, @mkitti, for your quick response!

---

<div class="post-metadata">

**Author:** ![Krastanov](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/krastanov/32/6817_2.png) [@Krastanov](https://discourse.julialang.org/u/Krastanov)\
**Post date:** [June 9, 2023, 2:53am UTC](https://discourse.julialang.org/t/simple-question-about-julia-types/100077/4 "2023-06-09T02:53:44Z")

</div>

For more context, there is this part of the manual:

[https://docs.julialang.org/en/v1/manual/types/#man-parametric-composite-types](https://docs.julialang.org/en/v1/manual/types/#man-parametric-composite-types)

Terms to research this more generally as a topic in computer science is invariant, covariant, and contravariant types: [Covariance and contravariance (computer science) - Wikipedia](https://en.wikipedia.org/wiki/Covariance_and_contravariance_%28computer_science%29)

~~My superficial understanding is that this subtyping rule is necessary in order to have reasonably fast type inference.~~

---

<div class="post-metadata">

**Author:** ![mmv](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/mmv/32/26323_2.png) [@mmv](https://discourse.julialang.org/u/mmv)\
**Post date:** [June 9, 2023, 9:34am UTC](https://discourse.julialang.org/t/simple-question-about-julia-types/100077/5 "2023-06-09T09:34:36Z")

</div>

Thanks a lot, @Krastanov, for those specific pointers to the documentation.

---

<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:** [June 9, 2023, 11:34am UTC](https://discourse.julialang.org/t/simple-question-about-julia-types/100077/6 "2023-06-09T11:34:35Z")

</div>

> [@Krastanov](#):
>
> My superficial understanding is that this subtyping rule is necessary in order to have reasonably fast type inference.

Well, there’s code that works when passed `Matrix{Number}` but fails for `Matrix{Float64}`. So it doesn’t make lots of sense to make `Matrix{Float64} <: Matrix{Number}`.  
Simple example of such code: `f(A::Matrix{Number}) = A[1] = big"10"^1000`.

---

<div class="post-metadata">

**Author:** ![bertschi](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/bertschi/32/33462_2.png) [@bertschi](https://discourse.julialang.org/u/bertschi)\
**Post date:** [June 9, 2023, 4:59pm UTC](https://discourse.julialang.org/t/simple-question-about-julia-types/100077/7 "2023-06-09T16:59:21Z")

</div>

The Julia docs seem rather clear in that the main reason for invariance is mutable types:

> 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)](https://en.wikipedia.org/wiki/Covariance_and_contravariance_%28computer_science%29). 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`](https://docs.julialang.org/en/v1/base/numbers/#Core.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.

Indeed, tuples – which are immutable – are covariant, e.g., `Tuple{Float64} <: Tuple{Number}` is true.

---

<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:** [June 9, 2023, 7:20pm UTC](https://discourse.julialang.org/t/simple-question-about-julia-types/100077/8 "2023-06-09T19:20:44Z")

</div>

> [@bertschi](#):
>
> The Julia docs seem rather clear in that the main reason for invariance is mutable types:

Nothing in those quotes from the docs refers to mutability. If it were possible to restrict subtypes of `Real` to only immutable types (i.e. `struct` instead of `mutable struct`), then the arguments in the docs around practicality would still apply.

One way to understand that `Point{Float64}` is not a subtype of `Point{Real}` is to note that `Point{Real}` is a concrete type, and concrete types cannot be subtyped. Of course, that begs the question, “How come concrete types can’t be subtyped?” Also, that argument does not apply to `AbstractMatrix{Number}`, since `AbstractMatrix{Number}` is not a concrete type.

So, it seems the best conceptual way to understand why `Foo{Float64}` is not a subtype of `Foo{Real}` is the observation pointed out by @aplavin: There are methods that work on `Foo{Real}` but not on `Foo{Float64}`, even when `Foo` is immutable. Here’s an example:

```julia
struct Foo{T}
    x::T
end

bar(foo::Foo) = typeof(foo)(big(foo.x)^100)

```

```julia
julia> bar(Foo{Real}(2))
Foo{Real}(1267650600228229401496703205376)

julia> bar(Foo{Int}(2))
ERROR: InexactError: Int64(1267650600228229401496703205376)

```

However, I’m not sure if that’s the best example, since it relies on introspection of the type of `foo` via `typeof`. Thoughts?

---

<div class="post-metadata">

**Author:** ![bertschi](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/bertschi/32/33462_2.png) [@bertschi](https://discourse.julialang.org/u/bertschi)\
**Post date:** [June 9, 2023, 7:59pm UTC](https://discourse.julialang.org/t/simple-question-about-julia-types/100077/9 "2023-06-09T19:59:37Z")

</div>

> [@CameronBieganek](#):
>
> Nothing in those quotes from the docs refers to mutability. If it were possible to restrict subtypes of `Real` to only immutable types (i.e. `struct` instead of `mutable struct`), then the arguments in the docs around practicality would still apply.

You’re right, there is no explicit reference to mutability. It was my interpretation/assumption that the memory layout should just play a role if mutating a data structure, i.e., trying to assign an object with `Point{Real}` layout to a place with an `Point{Float64}` layout.

In the end, the variance of data types is a design decision of the language effecting which invariants – in the sense of laws – can be assumed about functions. In particular, in functional languages such as Haskell the types provide a lot of information on what a function can or cannot do:

```haskell
generic :: [a] -> [a]
# Fully generic type, i.e., valid for all `a`
# => Function can at most change the list structure, but not look at elements!

parametric :: Show a => [a] -> String
# Function can assume/call `show` function on elements, but nothing else
# => will also work on all subtypes, i.e., with more specific constraints than `Show`. 

```

Dynamic languages are less stringent and especially with introspection – as in your example – any type assumptions can be broken. Guess this might be another of several reasons why _invariance_ was chosen as the save default in Julia.  
Interestingly tuples are an exception, such that `myfun(x::Real, y::AbstractString)` is implicitly considered the same as `yfun(x::T, y::S) where {T<:Real, S<:AbstractString}` saving a bit of thought and some characters of code. As far as I know there is also no alternative syntax, to force invariance in this case. Similarly, with abstract parametric types I would be curious about a real-world example where `myfun(x::AbstractVector{Real})` and maybe further methods are needed instead of the more generic `myfun(x::AbstractVector{T}) where {T <: Real}`.

---

<div class="post-metadata">

**Author:** ![danielwe](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/danielwe/32/35657_2.png) [@danielwe](https://discourse.julialang.org/u/danielwe)\
**Post date:** [June 9, 2023, 10:06pm UTC](https://discourse.julialang.org/t/simple-question-about-julia-types/100077/10 "2023-06-09T22:06:40Z")

</div>

> [@CameronBieganek](#):
>
> However, I’m not sure if that’s the best example, since it relies on introspection of the type of `foo` via `typeof`. Thoughts?

Here’s a less introspective example, though it’s more about containers than methods:

```julia
julia> struct Foo{T}
           x::T
       end

julia> x = Foo{Real}[Foo(3.0)]
ERROR: MethodError: Cannot `convert` an object of type
  Foo{Float64} to an object of type
  Foo{Real}

```

That is, `Foo{Float64}` doesn’t fit into a `Foo{Real}`-typed array.

* * *

But then what about tuples?

```julia
julia> x = Tuple{Real}[(3.0,)]
1-element Vector{Tuple{Real}}:
 (3.0,)

```

I suppose this works because `Tuple{Real}` is an abstract type, so under the hood `Vector{Tuple{Real}}` holds pointers like any other array with abstract eltype.

```julia
julia> isconcretetype(Tuple{Real})
false

julia> Tuple{Real} == Tuple{<:Real}
true

```

---

<div class="post-metadata">

**Author:** ![Oscar\_Smith](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/oscar_smith/32/25343_2.png) [@Oscar\_Smith](https://discourse.julialang.org/u/Oscar_Smith)\
**Post date:** [June 9, 2023, 10:28pm UTC](https://discourse.julialang.org/t/simple-question-about-julia-types/100077/11 "2023-06-09T22:28:11Z")

</div>

`Tuple`s are covariant unlike everything else for mostly historical reasons.

---

<div class="post-metadata">

**Author:** ![jling](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/jling/32/212909_2.png) [@jling](https://discourse.julialang.org/u/jling)\
**Post date:** [June 10, 2023, 2:25pm UTC](https://discourse.julialang.org/t/simple-question-about-julia-types/100077/13 "2023-06-10T14:25:13Z")

</div>

> [@Oscar\_Smith](#):
>
> for mostly historical reasons.

I believe it’s mostly because Tuple is used by type system itself (i.e function call argument types are put in a Tuple
