Aliasing two structs and having them be subtypes of an abstract type?

I am wondering if when I have two structs, say

mutable struct A{a, b}
   str_a::a
   str_b::b
end

mutable struct B{a, b, c}
   str_a::a
   str_b::b
   str_c::c
end

and I alias

const A_num = A{<:Number}
const B_num = B{<:Number}

is it possible to create an abstract type, such that A_num and B_num would be its subtypes without A and B being subtypes of some abstract type?

This is not possible.

2 Likes

Well that’s a shame. Would A and B have to be subtypes of the same abstract type for it to be possible?

yeah.

Could you explain the bigger picture? What are you trying to acccomplish?

Basically trying to avoid code duplication due to A and B being different types. I was hoping I could somehow hack an abstract type, say C, of which A and B would be a subtype and have f(x::C), rather than f(x::A) and f(x::B) (but for a lot of functions).

why do you need to add a type parameter at all?

Create a box type as follows. Does that help?

julia> abstract type AbstractBox end

julia> struct Box{T} <: AbstractBox
           x::T
           Box{T}(args...) where T= new{T}(T(args...))
       end

julia> Base.getproperty(b::Box, s::Symbol) = getproperty(getfield(b,:x), s)

julia> mutable struct A{a, b}
          str_a::a
          str_b::b
       end

julia> mutable struct B{a, b, c}
          str_a::a
          str_b::b
          str_c::c
       end

julia> const A_boxed = Box{A}
A_boxed (alias for Box{A})

julia> const B_boxed = Box{B}
B_boxed (alias for Box{B})

julia> x = A_boxed(1,2)
A_boxed(A{Int64, Int64}(1, 2))

julia> y = B_boxed(3,4,5)
B_boxed(B{Int64, Int64, Int64}(3, 4, 5))

julia> x isa AbstractBox
true

julia> y isa AbstractBox
true

julia> x.str_a
1

julia> x.str_b
2

julia> y.str_a
3

julia> y.str_b
4

julia> y.str_c
5

Edit: For completeness and tab completion:

Base.propertynames(x::Box) = Base.propertynames(getfield(x, :x))
1 Like

It’s a complex codebase with lots of functions that depending on the input type would have to do different things in certain parts.

This is very close to what I need, except that I can’t really do (otherwise it breaks with the rest of the code that is)

x = A_boxed(1, 2)
y = B_boxed(3, 4, 5)

but I have to keep

x = A_boxed(1, 2)
y = B_boxed(3, 4, 5)

Thank you for your attempts, but I think I’ll pursue a different approach.

You can always do

julia> mutable struct A{a, b}
          str_a::a
          str_b::b
       end

julia> mutable struct B{a, b, c}
          str_a::a
          str_b::b
          str_c::c
       end
julia> const A_num = A{<:Number, <:Number}
A_num (alias for A{<:Number, <:Number})

julia> const B_num = B{<:Number, <:Number, <:Number}
B_num (alias for B{<:Number, <:Number, <:Number})

julia> const AB_num = Union{A_num, B_num}
AB_num (alias for Union{A{<:Number, <:Number}, B{<:Number, <:Number, <:Number}})

julia> A("", 2) isa AB_num
false

julia> B(1//2, 2, 3/0) isa AB_num
true

julia> B(1//2, 2, 3/0)
B_num{Rational{Int64}, Int64, Float64}(1//2, 2, Inf)

What you cannot do is have one package create an abstract type encompassing both A_num and B_num (AB_num in the example) and then have another package define a new subtype struct C_num <: AB_num. For that kind of stuff, you should look at Holy traits.

1 Like