Composite type whose fields are all different suptypes of a supertype

I have a composite type whose fields are all different suptypes of a supertype. Can this be expresses in a more elagnat way then shown below?

struct Operators{A<:AbstractArray{Float64,2}, L<:AbstractArray{Float64,2}, D<:AbstractArray{Float64,2}, AH<:AbstractArray{Float64,2}, LH<:AbstractArray{Float64,2}, DH<:AbstractArray{Float64,2},AE<:AbstractArray{Float64,2}, LE<:AbstractArray{Float64,2}, DE<:AbstractArray{Float64,2}}
    𝕴::A
    𝕴ₑ::AE
    𝕴ₕ::AH
    𝔏::L
    𝔏ₑ::LE
    𝔏ₕ::LH
    𝔇::D
    𝔇ₑ::DE
    𝔇ₕ::DH
end
end

I thout about someting like

struct Operators{A, L, D, AH, LH, DH, L, LE, DE} where {A, L, D, AH, LH, DH, L, LE, DE} <:AbstractArray{Float64,2}
    𝕴::A
    𝕴ₑ::AE
    𝕴ₕ::AH
    𝔏::L
    𝔏ₑ::LE
    𝔏ₕ::LH
    𝔇::D
    𝔇ₑ::DE
    𝔇ₕ::DH
end

Any suggestions about style and how to do better are welcome.

You can enforce this on construction, but sadly it seems you can’t write new{typeof.(args)...}(args...) here:

julia> struct Operators2{A, L, D}
       𝕴::A
       𝔏::L
       𝔇::D
       Operators2(args::AbstractArray{Float64}...) = new{typeof(args[1]), typeof(args[2]), typeof(args[3])}(args...)
       end

julia> Operators2(rand(2,2), rand(2,2)', rand(2,3))
Operators2{Array{Float64,2},LinearAlgebra.Adjoint{Float64,Array{Float64,2}},Array{Float64,2}}([...
...])

Edit: you can do that if it isn’t attached to new, like so:

julia> struct Operators3{A, L, D}
       𝕴::A
       𝔏::L
       𝔇::D
       end

julia> Operators3(args::AbstractArray{Float64}...) = Operators3{typeof.(args)...}(args...);
julia> Operators3(args...) = error("nope");
julia> Operators3(A,L,D) = error("nope");

julia> Operators3(rand(2,2), rand(2,2), rand(2))
ERROR: nope

julia> Operators3(rand(2,2), rand(2,2))
ERROR: MethodError:
1 Like

Just don’t enforce types :slight_smile:

2 Likes

exactly what I was looking for

Operators3(args::AbstractArray{Float64}...) = Operators3{typeof.(args)...}(args...);

thx

Is there a specific reason why not? (what would the compiler say about that, would it make any difference in terms of performance…)

If I understand right it should make no difference to the compiler, 99% of the time.

Strongly enforcing types (like Float64) generally seems like a bad idea, as it means I won’t be able to use say ForwardDiff through your code, or some other exotic numbers.

But weakly enforcing that things are AbstractMatrixes etc. often seems like a good idea, so that when you give it obviously wrong arguments (e.g. because you forgot to splat a tuple somewhere) you get an error early on, not 15 steps into your calculation.

1 Like

The compiler will not care as long types are concrete. Just

https://docs.julialang.org/en/v1/manual/performance-tips/#Avoid-fields-with-abstract-type-1

and you will be fine.

1 Like

Basically it makes no performance difference as long as you use parametric types on your structs, as described in the performance tips.

Types are best used for method dispatch and occasionally to find/prevent bugs. But performance will be the same without them, and they are actually limiting the flexibility of your code.

1 Like

Ok thanks to all for teaching me this lesson. I should have read the performance tips in detail earlier. There one can find the nice sentence

Once one learns to appreciate multiple dispatch, there’s an understandable tendency to go overboard and try to use it for everything.

which describes exactly the situation I’m in.
So I just stay concrete with mild type enforcement…