Promotion rules for custom structs

I have a custom struct Body (shown below). For another struct, I’d like to be able to keep vectors of Body variables, and I’d like to be able to promote Body{Float32} to Body{Float64}, Body{Float64} to Body{BigFloat}, etc.

The following definition does run, but it still doesn’t allow for Body promotion.

julia> promote_rule(::Type{Body{A}}, ::Type{Body{B}}) where {A<:AbstractFloat, B<:AbstractFloat} = Body{promote_type(A, B)}

julia> b1 = Body(Float32.([1, 2, 3]u"m"), Float32.([1,2,3]u"m/s"), Float32(1u"kg"))

julia> b2 = Body(Float64.([1, 2, 3]u"m"), Float64.([1,2,3]u"m/s"), Float64(1u"kg"))

julia> promote(b1, b2)
ERROR: promotion of types Body{Float32} and Body{Float64} failed to change any arguments

Is what I’m trying to do possible?

Body Definition:

using Unitful
using StaticArrays

struct Body{F<:AbstractFloat}
   
    r̅::SVector{3, Unitful.Length{F}}
    v̅::SVector{3, Unitful.Velocity{F}}
    m::Unitful.Mass{F}

    function Body(r::R, v::V, m::M) where {
            T <: AbstractFloat, 
            R <: AbstractVector{Unitful.Length{T}}, 
            V <: AbstractVector{Unitful.Velocity{T}},
            M <: Unitful.Mass{T}
    }

        if length(r) ≢ length(v) ≢ 3
            error("The `Body` constructor requires 3 element vectors for position `r` and velocity `v`")
        else
            return new{T}(SVector{3}(T.(r)), SVector{3}(T.(v)), M(T(m)))
        end

    end

end

You need to make sure, you actually overload Base.promote_rule. What you are doing is defining a new function locally, that just happens to be called promote_rule.

5 Likes