UndefVarError for Type Variable

I’m sure I’m missing something obvious, but here we go. I defined methods that dispatch on the type of various “special matrices” from LinearAlgebra. Here is a MWE:

using LinearAlgebra

for type ∈ [:Symmetric, :LowerTriangular]
    @eval f(::Type{$type{T}}) where T = T
end

The expected result is returned when called on type LowerTriangular:

julia> f(LowerTriangular{Float64})
Float64

However, when called on Symmetric, I get:

julia> f(Symmetric{Float64})
ERROR: UndefVarError: T not defined
Stacktrace:
 [1] f(#unused#::Type{Symmetric{Float64, S} where S<:(AbstractMatrix{<:Float64})})
   @ Main ./REPL[5]:2
 [2] top-level scope
   @ REPL[10]:1

Interestingly, the following implementation works:

f(::Type{Symmetric{T, S}}) where {T, S} = T

julia> f(Symmetric{Float64, Matrix{Float64}})
Float64

Any idea?

Fully specifying the type vars works for me

using LinearAlgebra

for type ∈ [:Symmetric, :LowerTriangular]
    @eval f(::Type{$type{T, A}}) where {T, A} = T
end

@show f(LowerTriangular{Float64,Matrix{Float64}})
@show f(Symmetric{Float64,Matrix{Float64}})

yielding

f(LowerTriangular{Float64, Matrix{Float64}}) = Float64
f(Symmetric{Float64, Matrix{Float64}}) = Float64      

Don’t know if this helps though.

Yeah that works for me too. I just don’t understand how I can partially specify it for one type and not for the other.

I understand my job here as to make things work, sometimes without understanding why. So let us ask another question. What might be the sense of

@show LowerTriangular{Float32,Matrix{Float64}}([[1, 2], [3, 4], [5, 6]])

? Answer:

ERROR: LoadError: TypeError: in LowerTriangular, in S, expected S<:AbstractMatrix{Float32}, got Type{Matrix{Float64}}

So maybe this was designed before the invention of eltype?

This is simply due to the definition of LowerTriangular: here

But at least syntactically

abstract type MyAbstractTriangular{S<:AbstractMatrix} <: AbstractMatrix{eltype(S)} end

is accepted.

Edit: this seems to work, too:

struct MyTriangularMatrix{S<:AbstractMatrix} <: MyAbstractTriangular{S}
    A::S
end 

function MyTriangularMatrix{T}(A::Matrix{T}) where T 
end

tr = MyTriangularMatrix([(1, 2) (3, 4) (5, 6)])
println()

This might be an implementation detail? What are you more interested in: this detail or the general idea?