Reputation: 13
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
Reputation: 20960
By definition, the result of a promotion is one common type. So while you can just recursively promote the T
s, you have to resort to a common supertype for the S
s 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