# Why do we need the \`T\` in Symmetric{T, S}?

**URL:** <https://discourse.julialang.org/t/why-do-we-need-the-t-in-symmetric-t-s/128858>\
**Category:** General Usage\
**Tags:** question, linearalgebra, symmetric\
**Created:** [May 9, 2025, 1:49am UTC](https://discourse.julialang.org/t/why-do-we-need-the-t-in-symmetric-t-s/128858 "2025-05-09T01:49:11Z")\
**Posts on this page:** 17\
**Page:** 1

<div class="post-metadata">

**Author:** ![WalterMadelim](https://avatars.discourse-cdn.com/v4/letter/w/3e96dc/32.png) [@WalterMadelim](https://discourse.julialang.org/u/WalterMadelim)\
**Post date:** [May 9, 2025, 1:49am UTC](https://discourse.julialang.org/t/why-do-we-need-the-t-in-symmetric-t-s/128858/1 "2025-05-09T01:49:11Z")

</div>

I am a bit curious about the question in the title. Since it is unanswered in the docstring, I list its definition as a reference.

```julia
# Symmetric and Hermitian matrices
struct Symmetric{T,S<:AbstractMatrix{<:T}} <: AbstractMatrix{T}
    data::S
    uplo::Char

    function Symmetric{T,S}(data, uplo::Char) where {T,S<:AbstractMatrix{<:T}}
        require_one_based_indexing(data)
        (uplo != 'U' && uplo != 'L') && throw_uplo()
        new{T,S}(data, uplo)
    end
end

```

Since `Symmetric` is merely a “shell”, I wonder what is the purpose of `T` (the design that singles it out). Consider the following example

```julia
julia> using LinearAlgebra

julia> function m() return rand(-9:.1:9, 2, 2) end
m (generic function with 1 method)

julia> A = m()
2×2 Matrix{Float64}:
 1.9 3.7
 8.7 -1.8

julia> LinearAlgebra.symmetric_type(typeof(A[1, 1]))
Float64

julia> LinearAlgebra.symmetric_type(typeof(A))
Symmetric{Float64, Matrix{Float64}}

julia> B = [m() for i in 1:2, j in 1:2]
2×2 Matrix{Matrix{Float64}}:
 [8.6 2.1; 0.8 8.9] [0.7 -8.4; -2.3 -2.8]
 [6.4 4.1; -5.8 1.7] [4.6 -3.0; 4.5 5.6]

julia> LinearAlgebra.symmetric_type(typeof(B[1, 1]))
Symmetric{Float64, Matrix{Float64}}

julia> LinearAlgebra.symmetric_type(typeof(B))
Symmetric{AbstractMatrix, Matrix{Matrix{Float64}}}

```

See the last line, `AbstractMatrix` is hardly instructive.  
I mean, what about

 ![image](https://global.discourse-cdn.com/julialang/original/3X/4/b/4b3db21a68cd77fcc01b83378b349f78132f235f.png)

The related code is here

> <https://github.com/JuliaLang/LinearAlgebra.jl/blob/58003a59cf166a5f6da61d99af3946d7e40f232c/src/symmetric.jl#L92-L94>

(and line 60 therein).

---

<div class="post-metadata">

**Author:** ![Sukera](https://avatars.discourse-cdn.com/v4/letter/s/ce7236/32.png) [@Sukera](https://discourse.julialang.org/u/Sukera)\
**Post date:** [May 9, 2025, 6:34am UTC](https://discourse.julialang.org/t/why-do-we-need-the-t-in-symmetric-t-s/128858/2 "2025-05-09T06:34:53Z")

</div>

`T` here is the type of the elements inside of the `Symmetric`, indicated by `<: AbstractMatrix{T}` (where that same `T` designates the element type).

Are you perhaps looking for `eltype`?

---

<div class="post-metadata">

**Author:** ![WalterMadelim](https://avatars.discourse-cdn.com/v4/letter/w/3e96dc/32.png) [@WalterMadelim](https://discourse.julialang.org/u/WalterMadelim)\
**Post date:** [May 9, 2025, 7:14am UTC](https://discourse.julialang.org/t/why-do-we-need-the-t-in-symmetric-t-s/128858/3 "2025-05-09T07:14:42Z")

</div>

Yes. But then, isn’t this `T` redundant? making the type expression looks cumbersome. (I’ve revised the title of the topic). Is the `T` indispensable? We could have infer/dispatch conveniently, e.g. with the aid of `eltype` you suggested.

---

<div class="post-metadata">

**Author:** ![DanielVandH](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/danielvandh/32/31134_2.png) [@DanielVandH](https://discourse.julialang.org/u/DanielVandH)\
**Post date:** [May 9, 2025, 7:17am UTC](https://discourse.julialang.org/t/why-do-we-need-the-t-in-symmetric-t-s/128858/4 "2025-05-09T07:17:26Z")

</div>

You need the `T` so you can write `<: AbstractMatrix{T}` in the definition. You can’t just do `struct Symmetric{S}` or `struct Symmetric{S<:AbstractMatrix{T}}` (since this last one needs `T` to be defined first).

---

<div class="post-metadata">

**Author:** ![WalterMadelim](https://avatars.discourse-cdn.com/v4/letter/w/3e96dc/32.png) [@WalterMadelim](https://discourse.julialang.org/u/WalterMadelim)\
**Post date:** [May 9, 2025, 8:31am UTC](https://discourse.julialang.org/t/why-do-we-need-the-t-in-symmetric-t-s/128858/5 "2025-05-09T08:31:50Z")

</div>

Why not do this

```julia
julia> struct MySymmetric{S <: AbstractMatrix{T} where T <: Union{Number, AbstractMatrix}}
           data::S
       end

julia> using LinearAlgebra

julia> A = rand(2, 2); B = [rand(2, 2) for i in 1:2, j in 1:2];

julia> MySymmetric(A)
MySymmetric{Matrix{Float64}}([0.26880897363440703 0.6476423313174478; 0.6105024571886544 0.1442311885018166])

julia> LinearAlgebra.Symmetric(A)
2×2 Symmetric{Float64, Matrix{Float64}}:
 0.268809 0.647642
 0.647642 0.144231

julia> MySymmetric(B)
MySymmetric{Matrix{Matrix{Float64}}}([[0.8054973493291133 0.7597395285524011; 0.14616471160829714 0.42034533671996954] [0.737336878028752 0.8408610455649348; 0.48620788006404236 0.15674775620016002]; [0.8856275147903988 0.7373043349918851; 0.3587458066422289 0.32907006075331047] [0.8815327773307925 0.8533663448400511; 0.15092667149027095 0.18201636320297676]])

julia> LinearAlgebra.Symmetric(B)
2×2 Symmetric{AbstractMatrix, Matrix{Matrix{Float64}}}:
 [0.805497 0.75974; 0.75974 0.420345] [0.737337 0.840861; 0.486208 0.156748]
 [0.737337 0.486208; 0.840861 0.156748] [0.881533 0.853366; 0.853366 0.182016]

```

---

<div class="post-metadata">

**Author:** ![barucden](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/barucden/32/26154_2.png) [@barucden](https://discourse.julialang.org/u/barucden)\
**Post date:** [May 9, 2025, 8:44am UTC](https://discourse.julialang.org/t/why-do-we-need-the-t-in-symmetric-t-s/128858/6 "2025-05-09T08:44:36Z")

</div>

```julia-repl
julia> typeof(MySymmetric(A)) <: AbstractMatrix{Float64}
false

julia> typeof(Symmetric(A)) <: AbstractMatrix{Float64}
true

```

So you want to declare

```julia
struct MySymmetric{S <: AbstractMatrix{T}} <: AbstractMatrix{T} end

```

but that doesn’t work because `T` is not defined. You can fix it by adding the extra type parameter

```julia
struct MySymmetric{T, S <: AbstractMatrix{T}} <: AbstractMatrix{T} end

```

---

<div class="post-metadata">

**Author:** ![kellertuer](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/kellertuer/32/220707_2.png) [@kellertuer](https://discourse.julialang.org/u/kellertuer)\
**Post date:** [May 9, 2025, 8:47am UTC](https://discourse.julialang.org/t/why-do-we-need-the-t-in-symmetric-t-s/128858/7 "2025-05-09T08:47:15Z")

</div>

You can define that with a local `T`

```julia
struct MySymmetric{S<:AbstractMatrix} end
# or equivalently
struct MySymmetric{S<:AbstractMatrix{T where T}} end

```

but the main problem is then still that `MySymmetric{Matrix{Float64}` is never a subtype of `AbstractMatrix{Float64}` as one can see in the first `typeof` in the post above me. And that is something one wants for nice default dispatches for all `eltypes`.

---

<div class="post-metadata">

**Author:** ![WalterMadelim](https://avatars.discourse-cdn.com/v4/letter/w/3e96dc/32.png) [@WalterMadelim](https://discourse.julialang.org/u/WalterMadelim)\
**Post date:** [May 9, 2025, 9:41am UTC](https://discourse.julialang.org/t/why-do-we-need-the-t-in-symmetric-t-s/128858/8 "2025-05-09T09:41:28Z")

</div>

Yes, your last line is good. I copy-paste it here

```julia
struct MySymmetric{T, S <: AbstractMatrix{T}} <: AbstractMatrix{T} end

```

But the LinearAlgebra.jl define it as

```julia
struct Symmetric{T, S <: AbstractMatrix{<:T}} <: AbstractMatrix{T} end

```

We see that **the latter uses** `<: T`, what is the notivation? so that

> [@WalterMadelim](#):
>
> ```julia-auto
> julia> LinearAlgebra.Symmetric(B)
> 2×2 Symmetric{AbstractMatrix, Matrix{Matrix{Float64}}}:
> [0.805497 0.75974; 0.75974 0.420345] [0.737337 0.840861; 0.486208 0.156748]
> [0.737337 0.486208; 0.840861 0.156748] [0.881533 0.853366; 0.853366 0.182016]
> 
> ```

Looks a bit inconsistent.

* * *

> [@DanielVandH](#):
>
> You need the `T` so you can write `<: AbstractMatrix{T}` in the definition

> [@kellertuer](#):
>
> And that is something one wants for nice default dispatches

I now understand this point, thank you.

---

<div class="post-metadata">

**Author:** ![jishnub](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/jishnub/32/33620_2.png) [@jishnub](https://discourse.julialang.org/u/jishnub)\
**Post date:** [May 9, 2025, 9:55am UTC](https://discourse.julialang.org/t/why-do-we-need-the-t-in-symmetric-t-s/128858/9 "2025-05-09T09:55:01Z")

</div>

> [@WalterMadelim](#):
>
> We see that **the latter uses** `<: T`, what is the notivation?

I’m unsure if this is intentional, probably just an oversight that has not been corrected since.

---

<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:** [May 9, 2025, 10:53am UTC](https://discourse.julialang.org/t/why-do-we-need-the-t-in-symmetric-t-s/128858/10 "2025-05-09T10:53:32Z")

</div>

> [@WalterMadelim](#):
>
> ```julia
> struct Symmetric{T, S <: AbstractMatrix{<:T}} <: AbstractMatrix{T} end
> 
> ```

There is a [discussion in the relevant PR](https://github.com/JuliaLang/julia/pull/25688) but it did not clarify the issue for me, I don’t understand the possibility they mention.

---

<div class="post-metadata">

**Author:** ![barucden](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/barucden/32/26154_2.png) [@barucden](https://discourse.julialang.org/u/barucden)\
**Post date:** [May 9, 2025, 12:10pm UTC](https://discourse.julialang.org/t/why-do-we-need-the-t-in-symmetric-t-s/128858/11 "2025-05-09T12:10:45Z")

</div>

My understanding is that it’s because of matrices of matrices (`transpose` of a number has the same type, but `transpose` of a matrix is of type `Transpose`).

Example behavior:

```julia-repl
julia> A = [1 2;
            3 4]
2×2 Matrix{Int64}:
 1 2
 3 4

julia> B = [[A] [A];
            [A] [A]]
2×2 Matrix{Matrix{Int64}}:
 [1 2; 3 4] [1 2; 3 4]
 [1 2; 3 4] [1 2; 3 4]

julia> C = Symmetric(B)
2×2 Symmetric{AbstractMatrix, Matrix{Matrix{Int64}}}:
 [1 2; 2 4] [1 2; 3 4]
 [1 3; 2 4] [1 2; 2 4]

julia> C[1, 1]
2×2 Symmetric{Int64, Matrix{Int64}}:
 1 2
 2 4

julia> C[1, 2]
2×2 Matrix{Int64}:
 1 2
 3 4

julia> C[2, 1]
2×2 transpose(::Matrix{Int64}) with eltype Int64:
 1 3
 2 4

```

(three different eltypes, all of which are `<: AbstractMatrix`)

---

<div class="post-metadata">

**Author:** ![WalterMadelim](https://avatars.discourse-cdn.com/v4/letter/w/3e96dc/32.png) [@WalterMadelim](https://discourse.julialang.org/u/WalterMadelim)\
**Post date:** [May 9, 2025, 12:55pm UTC](https://discourse.julialang.org/t/why-do-we-need-the-t-in-symmetric-t-s/128858/12 "2025-05-09T12:55:52Z")

</div>

Do make sense. But in this case, `T` is not some “eltype” but a “least common eltype”. And in your example, `T` is `AbstractMatrix`, indicating that the dispatch advantage is lost. (But I guess this usage is not practical)

---

<div class="post-metadata">

**Author:** ![WalterMadelim](https://avatars.discourse-cdn.com/v4/letter/w/3e96dc/32.png) [@WalterMadelim](https://discourse.julialang.org/u/WalterMadelim)\
**Post date:** [May 9, 2025, 2:16pm UTC](https://discourse.julialang.org/t/why-do-we-need-the-t-in-symmetric-t-s/128858/13 "2025-05-09T14:16:23Z")

</div>

Wait a minute. There is still a question.  
The LinearAlgebra.jl defines

```julia
struct Symmetric{T, S <: AbstractMatrix{<:T}} <: AbstractMatrix{T} end

```

And you call `C = Symmetric(B)`, where `B isa Matrix{Matrix{Int64}}`.  
At the time of construction (which is “earlier”), `T` can surely be `Matrix{Int64}`.  
It’s after some manipulations (at a “later” time) that julia find `AbstractMatrix` can serve as a “least common supertype” for all the eltypes.  
The weird thing is: Did I tell julia that what role I want `T` to play? Did I stipulate that `T` should be the “least common supertype” at that “later” time? **Seems I hadn’t**.  
Then why can’t `T` also be `Matrix{Int64}`?

---

<div class="post-metadata">

**Author:** ![barucden](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/barucden/32/26154_2.png) [@barucden](https://discourse.julialang.org/u/barucden)\
**Post date:** [May 9, 2025, 2:29pm UTC](https://discourse.julialang.org/t/why-do-we-need-the-t-in-symmetric-t-s/128858/14 "2025-05-09T14:29:12Z")

</div>

> [@WalterMadelim](#):
>
> Did I tell julia that what role I want `T` to play?

You did not tell it explicitly. `T` is derived automatically by the code (not magically by Julia, by the code [LinearAlgebra.jl/src/symmetric.jl at ae208d6aae535461ba29812bbdbf6c6dcda9f6ce · JuliaLang/LinearAlgebra.jl · GitHub](https://github.com/JuliaLang/LinearAlgebra.jl/blob/ae208d6aae535461ba29812bbdbf6c6dcda9f6ce/src/symmetric.jl#L87)).

> [@WalterMadelim](#):
>
> And you call `C = Symmetric(B)`, where `B isa Matrix{Matrix{Int64}}`.  
> At the time of construction (which is “earlier”), `T` can surely be `Matrix{Int64}`.

If `T` was `Matrix{Int64}`, then `C[i, j]` should always return a `Matrix{Int64}`. That’s certainly possible, but it would involve copying when `C[i, j]` should be a transposed version of `B[i, j]`. Currently, we get just a lazy wrapper (of type `Transpose{Int64, Matrix{Int64}}`) – to avoid copies, I suppose.

In summary, `C[i, j]` can return `Transpose`, `Symmetric`, or `Matrix`, depending on `i, j`, and so `AbstractMatrix` is the narrowest common type (`Transpose <: AbstractMatrix`, too).

---

<div class="post-metadata">

**Author:** ![nsajko](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/nsajko/32/221187_2.png) [@nsajko](https://discourse.julialang.org/u/nsajko)\
**Post date:** [May 9, 2025, 2:32pm UTC](https://discourse.julialang.org/t/why-do-we-need-the-t-in-symmetric-t-s/128858/15 "2025-05-09T14:32:28Z")

</div>

> [@WalterMadelim](#):
>
> The [LinearAlgebra.jl](https://juliaregistries.github.io/General/packages/redirect_to_repo/LinearAlgebra) defines
> 
> ```julia
> struct Symmetric{T, S <: AbstractMatrix{<:T}} <: AbstractMatrix{T} end
> 
> ```
> 
> And you call `C = Symmetric(B)`, where `B isa Matrix{Matrix{Int64}}`.  
> At the time of construction (which is “earlier”), `T` can surely be `Matrix{Int64}`.

The assumption that construction happens earlier is wrong. For any value one wants to construct, the type must exist before the construction is eventually over (`new` returns). I don’t really understand the questions in your post, but one thing’s certain: the type system checks the constraints on the type parameters during type application, so before it creates the type, so before the value can be constructed. For example:

```julia-repl
julia> using LinearAlgebra
 
julia> Symmetric{Int, Int}
ERROR: TypeError: in Symmetric, in S, expected S<:(AbstractMatrix{<:Int64}), got Type{Int64}
Stacktrace:
 [1] top-level scope
   @ REPL[2]:1

```

---

<div class="post-metadata">

**Author:** ![WalterMadelim](https://avatars.discourse-cdn.com/v4/letter/w/3e96dc/32.png) [@WalterMadelim](https://discourse.julialang.org/u/WalterMadelim)\
**Post date:** [May 10, 2025, 12:55am UTC](https://discourse.julialang.org/t/why-do-we-need-the-t-in-symmetric-t-s/128858/16 "2025-05-10T00:55:09Z")

</div>

Yes, it is by the code in LinearAlgebra.jl. But the link you gave is not precise, since that `S` is very general therefore with no chance to be called. The actually code being called should be

> <https://github.com/JuliaLang/LinearAlgebra.jl/blob/ae208d6aae535461ba29812bbdbf6c6dcda9f6ce/src/symmetric.jl#L92-L94>

I propose to modify this function as `my_symmetric_type` as follows

```julia
julia> using LinearAlgebra

julia> @isdefined symmetric_type
false

julia> symmetric_type = LinearAlgebra.symmetric_type
symmetric_type (generic function with 4 methods)

julia> @isdefined promote_op
false

julia> promote_op = Base.promote_op
promote_op (generic function with 1 method)

julia> function my_symmetric_type(::Type{T}) where {S<:AbstractMatrix, T<:AbstractMatrix{S}}
         return Symmetric{typejoin(S, promote_op(transpose, S), symmetric_type(S)), T}
       end
my_symmetric_type (generic function with 1 method)

julia> A = rand(-9:9, 2, 2);

julia> M = [A for i in 1:2, j in 1:2]; t = typeof(M)
Matrix{Matrix{Int64}} (alias for Array{Array{Int64, 2}, 2})

julia> my_symmetric_type(t) # me proposed
Symmetric{AbstractMatrix{Int64}, Matrix{Matrix{Int64}}}

julia> LinearAlgebra.symmetric_type(t) # compared with the existing
Symmetric{AbstractMatrix, Matrix{Matrix{Int64}}}

```

Is it good?  
also @jishnub

---

<div class="post-metadata">

**Author:** ![WalterMadelim](https://avatars.discourse-cdn.com/v4/letter/w/3e96dc/32.png) [@WalterMadelim](https://discourse.julialang.org/u/WalterMadelim)\
**Post date:** [May 10, 2025, 12:59am UTC](https://discourse.julialang.org/t/why-do-we-need-the-t-in-symmetric-t-s/128858/17 "2025-05-10T00:59:48Z")

</div>

Thanks, it’s instructive.

Therefore my current guess about `Symmetric{T, S}` is:

1. `T` is the “least common supertype” of `eltype` of all the entries (finally after the symmetric matrix object has been constructed). The aim of defining this is to facilitate dispatch.
2. `S` is simply the type of the data stored.
