Union Type vs Inheritance from Abstract Type

If I have several structures:

struct Child1{N,T} end
struct Child2{T} end
const Child1OrChild2{N,T} = Union{Child1{N,T}, Child2{T}}

struct Mother{M,N,T}
  children::NTuple{M,Child1OrChild2{N,T}}
end 

Would it be possible to do similar thing by defining an abstract type instead of using Union Type? Like this:

abstract struct AbstractChild{N,T} end
struct Child1{N,T} <: AbstractChild{N,T}end
struct Child2{T} <: AbstractChild{N,T} end # this is not allowed

struct Mother{M,N,T}
  children::NTuple{M,AbstractChild{N,T}}
end 

I am still confused when to use inheritance from an abstract type and when to use Union type. What is the disadvantage of using Union type?

AFAICT, AbstractTypes are more extendable. You could make AbstractChild a subtype of AbstractDescendant (with its own specific descendent types) and dispatch based on that, for example. You could also add Child3, Child4, etc (in addition to Child1 and Child2) as instantiations of AbstractChild.

Union types can be more useful if you don’t need a bunch of generalizability. For example, AbstractVecOrMat =Union{AbstractArray{T,1}, AbstractArray{T,2}} is useful if you don’t want the full generality of an AbstractArray (e.g., exclude higher dimensional arrays).

Just remember that you normally want your struct fields to have concrete types, so instead of something like this,

abstract type AbstractChild end

struct Mother{N}
    children::NTuple{N, AbstractChild}
end

you would probably want something like this:

abstract type AbstractChild end

struct Mother{N, T <: AbstractChild}
    children::NTuple{N, T}
end
2 Likes

Thanks for reply. Is there any difference from the point view of performance?

Thanks for your answer! I modified my question a little bit. Would it be still possible to do it with abstract type.

Hm, I don’t think so. Unions used to be less efficient but this got fixed between Julia 0.7 and 1.0.

2 Likes

Yup, either of the following would work:

abstract type AbstractChild{N, T} end

struct Mother{M, C <: AbstractChild}
    children::NTuple{M, C}
end

or:

abstract type AbstractChild{N, T} end

struct Mother{M, N, T, C <: AbstractChild{N, T}}
    children::NTuple{M, C}
end
2 Likes

But, how could we make Child2{T} inherited from AbstractChild{N,T}?

Subtypes have to have at least as many type parameters as their parent types. So, if one of your AbstractChild subtypes only has one type parameter, then AbstractChild should have either one or zero type parameters. In other words, AbstractChild should only include parameters that are required in all subtypes of AbstractChild.

I start to be confused what is the benefit (or reason) of using

abstract type AbstractChild end

struct Mother{N}
    children::NTuple{N, AbstractChild}
end

in stead of using

abstract type AbstractChild end

struct Mother{N, T <: AbstractChild}
    children::NTuple{N, T}
end