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.
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).
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.
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).
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)
Wait a minute. There is still a question.
The LinearAlgebra.jl defines
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}?
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).
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> 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