Parametric types as subtypes of the type parameter

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?

1 Like

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?

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.

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

1 Like

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.

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.

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.

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

1 Like

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