Parametric types with parameters left unspecified as struct fields

parametric-types

#1

Suppose I have a mutable struct with a field used to store a sparse matrix, e.g.

mutable struct myStruct{Tv<:Number,Ti<:Integer}
    A::SparseMatrixCSC{Tv,Ti}
end

This works as expected for various combinations of Tv and Ti. Now suppose I was lazy, as I was yesterday and instead I defined

mutable struct mySimpleStruct
    A::SparseMatrixCSC
end

I understand that this is bad practice because the actual concrete type of A in instances of mySimpleStruct can’t be inferred. However, I had assumed that the mySimpleStruct construction would still allow me to work with any type of sparse matrix. However, I’ve observed the following behaviour, which I don’t understand:

>A = sprandn(10,10,0.1)
>A = convert(SparseMatrixCSC{Float64,Int32},A)
>mySimpleStruct(A)

  mySimpleStruct
  A → 10×10 SparseMatrixCSC{Float64,Int64} with 7 stored entries

As you can see, in storing my SparseMatrixCSC{Float64,Int32} matrix in a mySimpleStruct, the non-zero index storage was converted from Vector{Int32} to Vector{Int64}. I would have thought that specifying a field of a struct as just SparseMatrixCSC would allow it to store any type of sparse matrix but this doesn’t seem to be the case. Anyone know why the type conversion shown above is happening? I would have thought SparseMatrixCSC would behave like an abstract type with all the various concrete types SparseMatrixCSC{Tv,Ti} being subtypes. Why is that not the case? SparseMatrixCSC{Float64,Int32} <: SparseMatrixCSC returns true.

Thanks, Patrick


#2

Constructing a struct (with all of its parameters specified) or storing an element will, as I understand it, implicitly call convert(<the type of the field>, <the value>). If x isa T, then convert(T, x) is generally a no-op so you don’t see this behavior. However, SparseMatrixCSC is a bit odd in that respect:

julia> A = sprandn(10, 10, 0.1);

julia> A32 = convert(SparseMatrixCSC{Float64, Int32}, A);

julia> typeof(A32)
SparseMatrixCSC{Float64,Int32}

julia> typeof(convert(SparseMatrixCSC, A32))
SparseMatrixCSC{Float64,Int64}

this happens because of a method in sparsematrix.jl:

julia> @edit convert(SparseMatrixCSC, A32)

# should show the following in your editor:
# convert(::Type{SparseMatrixCSC}, M::AbstractMatrix{Tv}) where {Tv} = convert(SparseMatrixCSC{Tv,Int}, M)

#3

Oh, interesting. That explains it. Thanks a lot.


#4

Arguably this is a bug. Probably this should be defined

convert(::Type{SparseMatrixCSC}, M::SparseMatrixCSC)  = M