Type stability with chained parametric types


#1

Hi there! I have defined a custom type which includes both a vector of matrices, and a vector of matrix pairs, and am trying to achieve type stability. Here is a simplified version of my code:

using LinearAlgebra, InteractiveUtils

struct MatrixPair{U<:AbstractMatrix{<:Real}}
    A::U
    B::U
end

struct Matrices{T<:Real, U<:AbstractMatrix{T}, V<:AbstractVector{U}, W<:AbstractVector{MatrixPair{U}}}
    scalar::T
    matrices::V
    matrix_pairs::W
end

function sum(c::T, m::Matrices{T}) where T
    m.matrices[1] + m.matrix_pairs[1].A + m.matrix_pairs[1].B .+ (c + m.scalar)
end

M(m) = fill(Float64(m), 2, 2)

mp1 = MatrixPair(M(3), M(4))
mp2 = MatrixPair(M(5), M(6))
mat = Matrices(1.5, [M(1)], [mp1, mp2])

@code_warntype sum(3.14, mat)
display(sum(3.14, mat))

This works well in Julia 0.7, but in 0.6 (which I have to use for this project) I get:

MethodError: no method matching Matrices(::Float64, ::Array{Array{Float64,2},1}, ::Array{MatrixPair{Array{Float64,2}},1})

From what I can tell, this is due to no field in Matrices having type U, and a workaround is to create such a field in the Matrices struct: dummy::U, but this seems silly. Is there a better way? Can the type definition above be simplified/improved while keeping the type stability? Thanks!


#2

I got your code to work in 0.6.2 by defining a method that explicitly figures out the type parameters and does the construction. Then I replaced the constructor call in your code with a call to this method:

matrices(A, B, C) = Matrices{typeof(A), eltype(typeof(B)), typeof(B), typeof(C)}(A,B,C)

I agree that this seems a bit unnecessary.


#3

Ah, clever, that will work! I just changed it to an outer constructor Matrices(A, B, C), so that when I migrate to 0.7 / 1.0 I can remove that constructor without changing the callers.