Parametric types as subtypes of the type parameter


#1

I want my parametric types to be subtypes of the type parameter.

Consider a wrapper for floats:

struct wrapper{T} <: T where {T <: Union{Real}}
    bar::T
end

This example works fine. Now consider making the type T to be a subtype of Union{Real,Complex}. This throws an

invalid subtyping in definition of

Since Real and Complex are subtypes of Number, something such as

struct wrapper{T<: Union{Real, Complex}} <: Number
    bar::T
end

would be a possible solution for most cases.

However, this would be a quick fix and would defeat to the purpose of the parametric type to be a subtype of the type parameter. How does one achieve this?


#2

I don’t think it’s possible to make a subtype relation parametric; even in your first example, Union{Real} evaluates to just Real, so the supertype of any instance will always be Real, like

julia> wrapper(1)
wrapper{Int64}(1)

julia> supertype(typeof(ans))
Real

julia> wrapper("hey")
wrapper{String}("hey")

julia> supertype(typeof(ans))
Real

julia> wrapper(1.0)
wrapper{Float64}(1.0)

julia> supertype(typeof(ans))
Real

Maybe you can elaborate the use-case and why having the parametric subtype relation seems to be important?


#3

I am extending the ForwardDiff package to work with R –> C functions (currently it is limited to R –> R functions).

This requires the use of Dual Numbers, originally defined as

Dual{T,V<:Real,N} <: Real

where V is the “machine” type of the number (e.g., Float64) and T and N are not really important in this discussion.

I believe an extension to complex numbers would be correct with a redefinition such as

Dual{T,V,N} <: V where {V <: Union{Real, Complex}}

allowing the Dual number to either be Real or Complex but this seems to result in a

invalid subtyping in definition of

error.


#4

Why don’t you just use a function from R to R^2 for the derivative and then convert to a complex?

Eg see the realify function in IntervalRootFinding.jl


#6

Because I don’t think that should be the way to solve the problem. There have been issues reported since 2016 hinting for support for R –> C function differentiation and I thought I would take upon the task myself.
The end goal is to differentiate eigenvalues (hence R^n –> C^{m x m}) so I would stay away from such conversions, no?

But we digress. The question at hand is how to make a parametric type be a subtype of the type parameter with Unions.


#7

What you are asking for is not possible, because it would make the direct supertype of the declared type ambiguous.

To see why this is the case, consider

struct Wrapper{T<:Number} <: Number
    x::T 
end

Now, clearly Number is a supertype of Wrapper{Float64}. However, Wrapper (which you should think of as an abstract type) is also a supertype of Wrapper{Float64}. What is the supertype of Wrapper? Well, in this case Number is still a supertype of Wrapper, but if we had made it parametric as you are suggesting, it would be undefined. Julia does not allow this, since in Julia T is always a supertype of T{U}. This isn’t done arbitrarily, in fact this is one of the most common method signatures (e.g. ::AbstractVector is a very common type of function argument to see).

I’m not quite sure what you’re trying to do, but I suspect that pattern that would be best for you would be what I showed above. (Also note that the reason your first example worked without error was because it’s equivalent to what I showed above.) This still gives you a lot of flexibility, and you can still do it with arbitrarily complicated Union types.


#8

I don’t think it is possible to make a type subtype of Complex since it is not an abstract type, so the Complex will break the subtyping anyway, even it is not in a union.


#9

Could you make a Complex{Dual} instead of a Dual <: Complex?


#10

You don’t need to make it a subtype of Complex.