How to create convenient single-parametric types with multiple fields

Hello everyone!

MWE

I have a parametric type as follows:

struct Foster_Π{T <: Number}
    zero_zero::Bool
    H::T
    ω_z::Vector{T}
    ω_p::Vector{T}
end

I tried at first having a non-parametric type with everything as Float64, but I could not use autodiff with it. However, now that it is parametric, it is not “smart enough” to convert constructor arguments to the same type.

For example, this errors:

H = 1
ω_z1 = Float64[4.5e9,7e9]
ω_p1 = Float64[3e9,6e9]
F_Π = Foster_Π(zero_zero, H, ω_z1, ω_p1)

this does not:

H = 1.0
ω_z1 = Float64[4.5e9,7e9]
ω_p1 = Float64[3e9,6e9]
F_Π = Foster_Π(zero_zero, H, ω_z1, ω_p1)

Question

Is there any idiomatic way to automatically understand that the first case is just the second with an argument that casts into the rest?

I guess that using multiple dispatch, there could be Int methods that are automatically converted to the Float64 case, but having one of such methods for each field is not so funny :frowning:

Thanks for the help!

It works if you explicitly specify the type parameter,

Foster_Π{Float64}(zero_zero, H, ω_z1, ω_p1)

You can even force everything to other argument types, if the necessary convert functions exist, e.g.

Foster_Π{BigFloat}(zero_zero, H, ω_z1, ω_p1)

It probably wouldn’t be impossible for Julia to automatically create a constructor that coerces everything to a common type, but my guess for the motivation why this doesn’t exist is that as you say it might be quite complex, in additional to difficult to make type stable, and potentially not even be uniquely decidable, so its left to the user to specify explicitly.

1 Like

Yay, thanks!

I hadn’t even thought about explicitly naming the type :grinning_face_with_smiling_eyes:

You could also use an inner constructor that converts the fields for which coversion makes sense to type of the elements of the vectors:

julia> struct Foster_Π{T <: Number}
           zero_zero::Bool
           H::T
           ω_z::Vector{T}
           ω_p::Vector{T}
           Foster_Π(zero_zero,H,ω_z::Vector{T},ω_p::Vector{T}) where T = new{T}(zero_zero,T(H),ω_z,ω_p)
       end

julia> F_Π = Foster_Π(true, 1, [1.,2], [3.,4.])
Foster_Π{Float64}(true, 1.0, [1.0, 2.0], [3.0, 4.0])


(not that this will not convert the types of the vectors, but I guess that is fine, otherwise one would need to copy the data, which probably should be done explicitly if one wants to)