I’m currently writing a package, CliffordNumbers.jl, that makes the following abstract type declaration:
const BaseNumber = Union{Real,Complex}
abstract type AbstractCliffordNumber{Q<:QuadraticForm,T<:BaseNumber} <: Number
end
Clifford algebras are defined over real or complex numbers, and I wouldn’t want to include other objects that subtype Number
outside of the Real
and Complex
types provided by Base
, such as a Quaternion
type. In the code, I define an alias to simplify this.
I’ve defined a concrete type:
struct CliffordNumber{Q,T,L} <: AbstractCliffordNumber{Q,T}
data::NTuple{L,T}
end
but I get some very unexpected results when I work with subtyping relationships:
julia> supertype(CliffordNumber)
AbstractCliffordNumber{Q,T} where {Q,T}
I’d think that AbstractCliffordNumber
would be sufficient, but the results get stranger from here:
julia> CliffordNumber <: AbstractCliffordNumber
false
julia> CliffordNumber{APS} <: AbstractCliffordNumber{APS}
false
julia> CliffordNumber{APS,Float64} <: AbstractCliffordNumber{APS,Float64}
true
From my inspection of the behavior, it seems like Julia may not infer that the type constraints on T
in AbstractCliffordNumber{Q,T}
propagate to its subtypes if T
is a Union
, because this works:
julia> CliffordNumber{APS,<:Any} <: AbstractCliffordNumber{APS}
true
julia> CliffordNumber{APS,<:Union{Real,Complex}} <: AbstractCliffordNumber{APS}
true
Is this a bug in the implementation of subtyping, or am I doing something unreasonable enough to generate these results?