Avoiding certain abstract types in the fields of struct

I am trying to define a struct type for an algebraic functional form that has two “irregular” fields:

struct my_struct
     vars::NTuple{N, Int} where N
     func::Union{Function, Nothing}
     ...
end

The vars field includes the variable indices that are in the function (and hence it should have a variable length). The func field includes the function defined on those variables, but it should also be possible that there is no function assignment for the created object.

Based on my understanding, this is not a recommended format for defining these fields due to involving Abstract Types. I would appreciate any guidance on the best way to define these two fields.

You can simply put those types as type parameters onto my_struct like so:

struct my_struct{N,F}
     vars::NTuple{N, Int}
     func::F
     ...
end

Beware that this is actually a tradeoff! If you have many many many combinations of {N,F} then you will see a lot of compilation time because Julia specializes each function for the input types. So putting things into the type parameters is only worth it if you use a specific instance for long enough to amortize the associated compilation cost.

See also the relevant performance tip

6 Likes

Thanks a lot for the tip! I was also wondering if defining F as a subclass of Union{Function, Nothing} would have any benefits in this case?

struct my_struct{N,F<:Union{Function, Nothing}}
     vars::NTuple{N, Int}
     func::F
     ...
end

That would prevent an instance to be constructed with another type than those. But note that specifying the field inside the struct can be different than with the parametric type. Specifically, for example, if the struct is mutable, in one case you can mutate the value from one type to the other:

julia> mutable struct M
           x::Union{Nothing, Int}
       end

julia> m = M(1)
M(1)

julia> m.x = nothing

julia> m
M(nothing)

while in this case you cannot:

julia> mutable struct M2{T<:Union{Nothing,Int}}
           x::T
       end

julia> m2 = M2(1)
M2{Int64}(1)

julia> m2.x = nothing
ERROR: MethodError: Cannot `convert` an object of type Nothing to an object of type Int64
4 Likes

Oh I see! Thank you for the note!