Is there a way to restrict two fields in a struct to either be both AbstractTypeA
or both AbstractTypeB
, but not necessarily the same concrete type?
Note that the original poster on Slack cannot see your response here on Discourse. Consider transcribing the appropriate answer back to Slack, or pinging the poster here on Discourse so they can follow this thread.
(Original message ) (More Info)
Seems like a good question.
I can’t see an easy way to do this. Here’s an initial attempt which is a bit convoluted but seems to work. Also not sure about the implementation of common_supertype
function.
abstract type A end
abstract type B end
struct A1 <: A end
struct A2 <: A end
struct B1 <: B end
struct B2 <: B end
const Parents = Union{A,B}
#=
function common_supertype(x::X, y::Y) where {X, Y}
return first(intersect(supertypes(X), supertypes(Y)))
end
=#
function common_supertype(x::X, y::Y) where {X, Y}
supertypes_x = supertypes(X)
i = findfirst(S -> y isa S, supertypes_x)
return supertypes_x[i]
end
struct Bar{P<:Parents, X<:P, Y<:P}
x::X
y::Y
Bar{P,X,Y}(x::X, y::Y) where {P<:Parents, X<:P, Y<:P} = new{P,X,Y}(x, y)
end
function Bar(x::X, y::Y) where {X,Y}
P = common_supertype(x, y)
P <: Parents || throw(ArgumentError("Cannot construct Bar(x::$X, y::$Y) where arguments do not have a common supertype"))
Bar{P,X,Y}(x, y)
end
a1, a2 = A1(), A2()
b1, b2 = B1(), B2()
Bar(a1, a2)
Bar(b1, b2)
Bar(a1, b2)
Two solutions have been mentioned on slack.
One by @giordano using an extra type parameter:
julia> struct Foo{T<:Union{Integer,AbstractFloat}, A<:T, B<:T}
a::A
b::B
end
julia> Foo{AbstractFloat,Float32,Float64}(1.0, 2.0)
Foo{AbstractFloat, Float32, Float64}(1.0f0, 2.0)
julia> Foo{Integer,UInt8,Int32}(1, 2)
Foo{Integer, UInt8, Int32}(0x01, 2)
julia> Foo{Integer,Float64,Int32}(1.0, 2)
ERROR: TypeError: in Foo, in A, expected A<:T<:Union{AbstractFloat, Integer}, got Type{Float64}
Stacktrace:
[1] top-level scope
@ REPL[4]:100:
One by @jakobnissen by ensuring type equality during construction:
struct Foo{A, B}
x::A
y::B
function Foo{A, B}(x::A, y::B) where {A, B}
if !((A <: Integer && B <: Integer) || (A <: AbstractFloat && B <: AbstractFloat))
error("In Foo, Types A and B must have same supertype")
end
new{A, B}(x, y)
end
end