Malcolm Davy
Malcolm Davy

Reputation: 13

Julia: Defining promote_rule with multiple parametric types

Say I want to define a promote_rule() for a type that has multiple parametric types, for example for type MyType:

abstract type State end
struct Open<:State end
struct Closed<:State end

struct MyType{T,S<:State}
    x::T
    state::S
end

Is there a way to define a promote_rule() which only promotes the first type and not the second, for example:

myFloat = MyType(1.0, Open()) # MyType{Float64, Open}
myInt = MyType(2, Closed()) # MyType{Int64, Closed}
promote(myFloat, myInt)
# (MyType{Float64, Open}, MyType{Float64, Closed})

Upvotes: 1

Views: 313

Answers (1)

phipsgabler
phipsgabler

Reputation: 20960

By definition, the result of a promotion is one common type. So while you can just recursively promote the Ts, you have to resort to a common supertype for the Ss if you want to keep them as is. Simply using State would be a valid choice, but a Union leads to a bit more fine-grained results:

julia> Base.promote_rule(::Type{MyType{T1, S1}}, ::Type{MyType{T2, S2}}) where {T1, T2, S1, S2} = MyType{promote_type(T1, T2), <:Union{S1, S2}}

julia> promote_type(MyType{Int, Closed}, MyType{Float64, Closed})
MyType{Float64,#s12} where #s12<:Closed

julia> promote_type(MyType{Int, Closed}, MyType{Float64, Open})
MyType{Float64,#s12} where #s12<:Union{Closed, Open}

You still have to define the respective convert methods for promote to work, of course; specifically, one ignoring the state type:

julia> Base.convert(::Type{<:MyType{T}}, m::MyType) where {T} = MyType(convert(T, m.x), m.state)

julia> promote(myFloat, myInt)
(MyType{Float64,Open}(1.0, Open()), MyType{Float64,Closed}(2.0, Closed()))

But be sure to test all kinds of combinations really well. Promotion and conversion is really fiddly and hard to get right the first time, in my experience.

Upvotes: 2

Related Questions