Union troubles

I want to define a type that is effectively a vector of a matrix of operators. However, if the operator has norm zero or is proportional to the identity then we can simplify the necessary operations => we want to store this info. I wanted to use Union{E,M} where {E<: Number,M<:Operator}. If an element e::E then the corresponding operator is simply e*idenitty.

Now the following happens:

function initialize(x::Vector{Matrix{Union{E,M}}}) where {E<:Number,M<:Matrix}
E == eltype(M) || throw(ArgumentError(“scalar type should match opp eltype E ≠ (eltype(M))”))
end

initialize([Matrix{Union{Int64,Matrix{Int64}}}(fill(2,3,3))])

fails with

ERROR: ArgumentError: scalar type should match opp eltype Union{Int64, Array{Int64,2}} ≠ Int64

What am I missing?
Is there a better design?

At the moment I’m just going with a 3d array instead of Vector{Matrix{}} but I’m a bit unhappy with this, as the vector in reality is only periodic in it’s first argument, and I’m not really use a vector but some periodic vector type.

I think if you change the where to

where {E<:Number, M<:Matrix{E}}

then you don’t even need the type check within the body.

Fair enough, but I still don’t understand why it errors out. After all, the error message implies that E is Union{Int64, Array{Int64,2}} while the function signature specifies that E has to subtype number.

You are correct, this also intrigues me. I would write code with the assumption that E <: Number as it is enforced by the where clause of your function. However, showing E gives:

E = Union{Int64, Array{Int64,2}}
E <: Number = false

Would it be a bug in Julia, or it is just that where clauses do not work as we assume they work?

I also think it’s a bug. I can’t really use the workaround given here because my “operators” are abstract tensor maps from a different package, for which the element type is not defined… Shall I open a bug report on github?

I think the ideal would be first catching the attention of a JuMP developer here in Discourse, but if this does not happen, these are the guidelines for submitting a bug report to Julia.

As a workaround, maybe E>:eltype(M) would work?

That does look like a bug, as two method signatures that Julia sees as identical work differently:

julia> foo(x::T) where {E<:Number, M<:Matrix, T<:Matrix{Union{E,M}}} = E, M
foo (generic function with 1 method)

julia> foo(Matrix{Union{Int64, Matrix{Int64}}}(fill(2,3,3)))
(Union{Int64, Array{Int64,2}}, Array{Int64,2})

julia> foo(x::Matrix{Union{E,M}}) where {E<:Number, M<:Matrix} = E, M
foo (generic function with 1 method)

julia> foo(Matrix{Union{Int64, Matrix{Int64}}}(fill(2,3,3)))
(Int64, Array{Int64,2})

I just pushed the code with less type specifications (enough to pass unit tests and work for my use cases) and I’m now getting segfaults on other versions of julia … Bug report is open where clause fails with union · Issue #35853 · JuliaLang/julia · GitHub