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
``````

``````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))
...])
``````

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

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 `AbstractMatrix`es 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β¦