# Removing specific Type from Union

**URL:** <https://discourse.julialang.org/t/removing-specific-type-from-union/98943>\
**Category:** Performance\
**Tags:** type\
**Created:** [May 16, 2023, 3:45pm UTC](https://discourse.julialang.org/t/removing-specific-type-from-union/98943 "2023-05-16T15:45:17Z")\
**Posts on this page:** 14\
**Page:** 1

<div class="post-metadata">

**Author:** ![RainerHeintzmann](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/rainerheintzmann/32/19726_2.png) [@RainerHeintzmann](https://discourse.julialang.org/u/RainerHeintzmann)\
**Post date:** [May 16, 2023, 3:45pm UTC](https://discourse.julialang.org/t/removing-specific-type-from-union/98943/1 "2023-05-16T15:45:17Z")

</div>

I am trying to find a way to remove a specific type from a Union of types. In the code below, I would like to inherit from `AbtractArray{T,N}` if `R` is `Nothing` but otherwise inherit from the `Union{T,R}`:

```julia
julia> struct MyT{T,N,R} <: AbstractArray{ifelse(R isa Type{Nothing}, T, Union{T,R}), N}
              parent
       end

julia> foo(a :: AbstractArray{T,N}) where {T,N} = println("Type: $T Dims:$N")
foo (generic function with 1 method)

julia> q = MyT{Float64, 2, Nothing}(rand(3,4));

julia> foo(q)
Type: Union{Nothing, Float64} Dims:2

```

This does not work (since in the definition the types are a `TypeVar`), so I tried using `typeintersect` but there the problem is that I don’t know how to define a type which includes all types BUT `Nothing` such that I can use `typeintersect`. Another alternative would be to have a function `typesubtract` which removes a specific type from a union. This seems to be [possible](https://discourse.julialang.org/t/how-to-get-the-non-nothing-type-from-union-t-nothing/30523/2) with this code:

```julia
remove_nothing_type(::Type{T}) where {T} = Core.Compiler.typesubtract(T, Nothing, 2)

```

But this approach causes an error `ERROR: LoadError: UndefVarError: `T` not defined`, if called inside the type definition.  
Any ideas how to get this to work?

---

<div class="post-metadata">

**Author:** ![RainerHeintzmann](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/rainerheintzmann/32/19726_2.png) [@RainerHeintzmann](https://discourse.julialang.org/u/RainerHeintzmann)\
**Post date:** [May 16, 2023, 4:10pm UTC](https://discourse.julialang.org/t/removing-specific-type-from-union/98943/2 "2023-05-16T16:10:06Z")

</div>

… to add to the above: `typeintersect` also seems to fail if used in the struct definition:

```julia
julia> struct MyT{T,N,R} <: AbstractArray{typeintersect(Union{T, Missing}, Union{T,R}), N}
                     parent end

julia> q = MyT{Float64, 2, Nothing}(rand(3,4));

julia> foo(q)
Type: Union{Missing, Nothing, Float64} Dims:2

```

---

<div class="post-metadata">

**Author:** ![Benny](https://avatars.discourse-cdn.com/v4/letter/b/49beb7/32.png) [@Benny](https://discourse.julialang.org/u/Benny)\
**Post date:** [May 16, 2023, 5:48pm UTC](https://discourse.julialang.org/t/removing-specific-type-from-union/98943/3 "2023-05-16T17:48:10Z")

</div>

It’s actually kind of horrifying that your `struct` definition runs because `isa` and `typeintersect` can technically accept `TypeVar`s. For example this would throw an error:

```julia
abstract type A{N} end
struct B{N} <: A{ifelse(N>0, N, -N)} end
# error: MethodError: no method matching isless(::Int64, ::TypeVar)

```

AFAIK there’s no way to do any meaningful computation on type parameters in the `struct` header. The calls in the type parameters are not delayed until the type exists, it just immediately runs on the `T::TypeVar` you gave it. It wouldn’t make sense in general for subtyping to have complex runtime behavior, imagine if I put a call that could return any one of multiple types like a `rand` call, I could make 1 concrete type have different abstract types at different times. I think your best bet is matching the `T` in the concrete type and the supertype, and you can specify whether that is a `Float64` or `Union{Float64, Nothing}` at construction.

---

<div class="post-metadata">

**Author:** ![RainerHeintzmann](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/rainerheintzmann/32/19726_2.png) [@RainerHeintzmann](https://discourse.julialang.org/u/RainerHeintzmann)\
**Post date:** [May 16, 2023, 6:57pm UTC](https://discourse.julialang.org/t/removing-specific-type-from-union/98943/4 "2023-05-16T18:57:36Z")

</div>

In my case such a type calculation is wanted, since I am “misusing” the homemade class CircShift as a dummy to fill as pad values in a ShiftedArray which then gets to be a CircShiftedArray. This may be a little bit hacky but I did not want to copy/paste large amounts of broadcasting boilerplate code between the two array types. Subtyping via an Abstract class may be an option. maybe I should look into this to find out how to share the code in this way. Are there any existing abstract SubTypes of AbstractArray which have some boilerplate code for broadcasting? I guess ShiftedArrays.jl, CircShiftedArrays, PaddedArrays, CatViews all are in need of something similar to properly work with CUDA.jl.

---

<div class="post-metadata">

**Author:** ![Benny](https://avatars.discourse-cdn.com/v4/letter/b/49beb7/32.png) [@Benny](https://discourse.julialang.org/u/Benny)\
**Post date:** [May 16, 2023, 11:49pm UTC](https://discourse.julialang.org/t/removing-specific-type-from-union/98943/5 "2023-05-16T23:49:32Z")

</div>

`AbstractArray` itself has broadcasting, you just need to implement some interface methods it needs. But that’s a different topic, the subtyping problem here comes first. The thing is, subtyping isn’t flexible enough to put concrete types within arbitrary abstract types, and they do have to share type parameters one-to-one, there’s no `A{T} <: B{f(T)}` except when `A = B = Tuple; f = supertype`.

Is there no way for you to refactor? There’s nothing wrong with `MyT{Union{Float64, Int64}, 2}`, it’s much clearer that way to show an array’s element type, especially if you’re already wrapping a parent array. If you want to get rid of the `Nothing`, you should copy the elements of your parent array to an array that can’t contain `nothing`, then wrap the new array instead. If I had a type `MyT{Float64, 2, Nothing}`, I’d expect it to be able to contain a `nothing` somewhere, so I’d prefer a `MyT{Float64, 2}` if I can’t have `nothing`s.

---

<div class="post-metadata">

**Author:** ![RainerHeintzmann](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/rainerheintzmann/32/19726_2.png) [@RainerHeintzmann](https://discourse.julialang.org/u/RainerHeintzmann)\
**Post date:** [May 17, 2023, 6:26am UTC](https://discourse.julialang.org/t/removing-specific-type-from-union/98943/6 "2023-05-17T06:26:04Z")

</div>

Thanks a lot for these insights. Refactoring is for my application definately not the way to go.  
I think I found a solution to my problem. The key insight was that the constructor has to be deciding which type to use:

```julia
struct MyType{T,N,R} <: AbstractArray{T, N}
           parent
           function MyType(myparent::AbstractArray{T, N}, default::R =nothing) where {T,N,R}
               new{remove_nothing_type(Union{T,R}), N, R}(myparent)
      end
end

```

Or even better by using multiple dispatch with multiple constructures for type stability.

As for the other issue with broadcasting: Yes, there is broadcasting in `AbstractArray` but this seems to assume that “get\_index” can be called at will. Yet, when trying to avoid calls to “get\_index”, which seems a necessity for the `CuArray` type, a surprisingly large amount of [broadcasting code](https://github.com/JuliaArrays/ShiftedArrays.jl/pull/66) had to be written.

---

<div class="post-metadata">

**Author:** ![Benny](https://avatars.discourse-cdn.com/v4/letter/b/49beb7/32.png) [@Benny](https://discourse.julialang.org/u/Benny)\
**Post date:** [May 17, 2023, 7:56am UTC](https://discourse.julialang.org/t/removing-specific-type-from-union/98943/7 "2023-05-17T07:56:34Z")

</div>

Inner constructor constraints make more sense because types do exist then, and maybe it can happen at compile-time.  
There’s still problems, which I comment in the code example below. I don’t know of an implementation of `remove_nothing_type` so I just ad-libbed. I think the thing that stands out to me is that the same type can wrap parent arrays with different `eltype`s, which may not match the wrapper type’s supertype.

```julia
julia> begin
       remove_nothing_type(::Type{T}) where T = Base.typesplit(T, Nothing)
       struct MyType{T,N,R} <: AbstractArray{T, N}
         parent # not annotated, so defaults to ::Any
         function MyType(myparent::AbstractArray{T, N}, default::R =nothing) where {T,N,R}
           new{remove_nothing_type(Union{T,R}), N, R}(myparent)
         end
       end
       x = MyType([1]) # need to suppress REPL display, <:AbstractArray needs implementation
       y = MyType(Union{Int, Nothing}[1, nothing])
       z = MyType(Union{Int, Float64}[1, 1.5], 0.0) # weird need to instantiate R
       nothing
       end

julia> typeof(x), typeof(x.parent) # parent eltype matches
(MyType{Int64, 1, Nothing}, Vector{Int64})

julia> typeof(y), typeof(y.parent) # parent eltype does not match
(MyType{Int64, 1, Nothing}, Vector{Union{Nothing, Int64}})

julia> typeof(y) <: AbstractVector{Int64}, typeof(y.parent) <: AbstractVector{Int64}
(true, false)

julia> typeof(z), typeof(z.parent) # this is not what you intended
(MyType{Union{Float64, Int64}, 1, Float64}, Vector{Union{Float64, Int64}})

```

---

<div class="post-metadata">

**Author:** ![RainerHeintzmann](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/rainerheintzmann/32/19726_2.png) [@RainerHeintzmann](https://discourse.julialang.org/u/RainerHeintzmann)\
**Post date:** [May 17, 2023, 1:47pm UTC](https://discourse.julialang.org/t/removing-specific-type-from-union/98943/8 "2023-05-17T13:47:06Z")

</div>

Thanks! The implementation of `remove_nothing_type` was stated at the very top in this thread 😉

```julia
remove_nothing_type(::Type{T}) where {T} = Core.Compiler.typesubtract(T, Nothing, 2)

```

But yes, The type of the array is not always the type of its parent. But this is actually totally fine and the whole point. In the [actual usecase](https://github.com/JuliaArrays/ShiftedArrays.jl/pull/66) the `ShiftedArray` can also return `missing` for elements of the shifted array, which are outside the original boundaries. This all depends on the type of the `default` argument. Since the array needs to return the correct type on calls of `similar` etc. we need to change both, the answer to `eltype` and the basetype of the `AbstractArray` to cover all use-cases.

---

<div class="post-metadata">

**Author:** ![Benny](https://avatars.discourse-cdn.com/v4/letter/b/49beb7/32.png) [@Benny](https://discourse.julialang.org/u/Benny)\
**Post date:** [May 17, 2023, 8:01pm UTC](https://discourse.julialang.org/t/removing-specific-type-from-union/98943/9 "2023-05-17T20:01:28Z")

</div>

> [@RainerHeintzmann](#):
>
> The type of the array is not always the type of its parent.

I mostly expect `eltype(w) === eltype(parent(w))`, like `CircShiftedArray`s. The exceptions should be predictable like `ShiftedArray` including `missing`s, and even then that’s a separate parameter `M` from the `T` parameter, which it does share with its `parent`. I’m not exactly sure what sort of `ShiftedArray` variant you are making, but having a wrapper’s type parameters not fully dependent on the parent’s type paremeters seems really hard to work with, even if you do annotate the `.parent` field’s type.

---

<div class="post-metadata">

**Author:** ![RainerHeintzmann](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/rainerheintzmann/32/19726_2.png) [@RainerHeintzmann](https://discourse.julialang.org/u/RainerHeintzmann)\
**Post date:** [May 17, 2023, 8:20pm UTC](https://discourse.julialang.org/t/removing-specific-type-from-union/98943/10 "2023-05-17T20:20:14Z")

</div>

I think the functionality of the original `ShiftedArray` and `CircShiftedArray` implementation is now fully supported and the `CircShiftedArray` is actually a `ShiftedArray` with a different type `default` parameter. Click on [this link](https://github.com/JuliaArrays/ShiftedArrays.jl/pull/66) to look at the code (and comment), if this is of interest. `ShiftedArray` previously also did not generally adhere to `eltype(w) === eltype(parent(w))`, which is why the revised implementation had to also adhere to this sheme.

---

<div class="post-metadata">

**Author:** ![Benny](https://avatars.discourse-cdn.com/v4/letter/b/49beb7/32.png) [@Benny](https://discourse.julialang.org/u/Benny)\
**Post date:** [May 17, 2023, 9:22pm UTC](https://discourse.julialang.org/t/removing-specific-type-from-union/98943/11 "2023-05-17T21:22:02Z")

</div>

This is an interesting refactorization, `ShiftedArray` and the `AbstractArray`’s element type don’t have the `M` parameter anymore. So what happens if you run the doc’s example (or its equivalent) and check the `eltype`? Is the `Missing` still there, and if so how did you put it back after removing `M` from the `AbstractArray`?

```julia
julia> v = reshape(1:16, 4, 4);
julia> s = ShiftedArray(v, (2, 0));
julia> eltype(s)

```

This is more just me asking a question, as for your topic, I think you’re on the right track handling it in an inner constructor.

---

<div class="post-metadata">

**Author:** ![RainerHeintzmann](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/rainerheintzmann/32/19726_2.png) [@RainerHeintzmann](https://discourse.julialang.org/u/RainerHeintzmann)\
**Post date:** [May 18, 2023, 9:43am UTC](https://discourse.julialang.org/t/removing-specific-type-from-union/98943/12 "2023-05-18T09:43:51Z")

</div>

I don’t quite understand what you mean by the `M` parameter. Presumably you are referring to `N` which is present in the inheritance but NOT in the parent array used in the contruction. Indeed. This allowed these two to differ.

```julia
julia> v = reshape(1:16, 4, 4);

julia> s = ShiftedArray(v, (2, 0));

julia> eltype(s)
Union{Missing, Int64}

```

Which is what is expected and wanted.

---

<div class="post-metadata">

**Author:** ![Benny](https://avatars.discourse-cdn.com/v4/letter/b/49beb7/32.png) [@Benny](https://discourse.julialang.org/u/Benny)\
**Post date:** [May 18, 2023, 5:54pm UTC](https://discourse.julialang.org/t/removing-specific-type-from-union/98943/13 "2023-05-18T17:54:59Z")

</div>

Skimming through the link to your ocmmits, I saw this change to the `ShiftedArray` struct in shiftedarray.jl:

```julia
-struct ShiftedArray{T, M, N, S<:AbstractArray} <: AbstractArray{Union{T, M}, N}
- parent::S
+struct ShiftedArray{T, N, A<:AbstractArray, R} <: AbstractArray{T, N} 
+ parent::A

```

and wondered how `eltype` would replicate the prior `Union{T, M}`, but now that I look again, I see that `shifted_array_base_type(Tb, default)` takes care of that in the inner constructor.

If `ShiftedArray` with an `R` is your basis for the `MyType` example here, I would say that if I can `getindex` the `default` value at any indices, I fully expect the array’s `eltype` to be `Union{T, R}`. If the `default` value is not present at any index, I think you can use your approach when `R=CircShift` where you just return `T`, doesn’t seem necessary to `remove_nothing_type(Union{T,R})` then.

---

<div class="post-metadata">

**Author:** ![RainerHeintzmann](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/rainerheintzmann/32/19726_2.png) [@RainerHeintzmann](https://discourse.julialang.org/u/RainerHeintzmann)\
**Post date:** [May 18, 2023, 6:15pm UTC](https://discourse.julialang.org/t/removing-specific-type-from-union/98943/14 "2023-05-18T18:15:54Z")

</div>

Correct. This is why the code does not use `remove_nothing_type` or `remove_circshift_type` but simply uses the contructor.
