Parameterising types with multiple parameters

I have a type parameterised by floating point precision, something like this:

struct MyType{T <: AbstractFloat}
    x::T
    data::Array{T}
end

Note that it’s import that the type of x and the type of the array data match.

And I want this to also work on the GPU, so basically I also want this to be valid:

struct MyType{T <: AbstractFloat}
    x::T
    data::CuArray{T}
end

My natural attempt to resolve this is to provide an additional type parameter, something like:

struct MyType{T <: AbstractFloat, P <: AbstractArray}
    x::T
    data::P{T}
end

However, Julia complains TypeError: in Type{...} expression, expected UnionAll, got a value of type TypeVar.

Two questions:

  1. Why isn’t this valid?
  2. What is the most natural (and simple) way to express this kind of type and constraint?
  1. (Simplified) It doesn’t work because P could already match a concrete type. For example, P could be Array{Float64,1}, so P{T} is not valid.
  2. Use
    struct MyType{T <: AbstractFloat, P <: AbstractArray{T}}
        x::T
        data::P
    end
    
    instead.

Note also that in your original example, data::Array{T} is not a concrete type (the second type parameter is missing), so this would slow down your code.

The following might help:

struct MyType{T <: AbstractFloat, P <: AbstractArray{T}}
    x::T
    data::P
end

x = MyType(1.0, Vector{Float64}(undef, 1))
println(typeof(x))
y = MyType(1.0, Matrix{Float64}(undef, 1, 1))
println(typeof(y))
z = MyType(1.0, Matrix{Int}(undef, 1, 1))
println(typeof(z))

yielding

MyType{Float64, Vector{Float64}}
MyType{Float64, Matrix{Float64}}
ERROR: LoadError: MethodError: no method matching MyType(::Float64, ::Matrix{Int64})

Thank you! Exactly what I was looking for.