# Unbound type parameters detected in Union{Missing,T}

**URL:** <https://discourse.julialang.org/t/unbound-type-parameters-detected-in-union-missing-t/129042>\
**Category:** Tooling\
**Tags:** aqua\
**Created:** [May 15, 2025, 2:52pm UTC](https://discourse.julialang.org/t/unbound-type-parameters-detected-in-union-missing-t/129042 "2025-05-15T14:52:59Z")\
**Posts on this page:** 10\
**Page:** 1

<div class="post-metadata">

**Author:** ![FerreolS](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/ferreols/32/216854_2.png) [@FerreolS](https://discourse.julialang.org/u/FerreolS)\
**Post date:** [May 15, 2025, 2:53pm UTC](https://discourse.julialang.org/t/unbound-type-parameters-detected-in-union-missing-t/129042/1 "2025-05-15T14:53:00Z")

</div>

Hi,  
Aqua.jl complains about `Unbound type parameters detected` in the following MWE:

```julia-repl
julia> module mymod
       
       function foo(x::AbstractArray{<:Union{T,Missing}}) where {T<:Real}
           m = .!ismissing.(x) .&& .!isnan.(x)
           return [ifelse.(m, x, T(0))]
       end
       
       function foo(x::AbstractArray{<:Missing})
           m = .!ismissing.(x) .&& .!isnan.(x)
           return [ifelse.(m, x, 0)]
       end
       end
Main.mymod

julia> Aqua.test_unbound_args(mymod)
Unbound type parameters detected:
[1] foo(x::AbstractArray{<:Union{Missing, T}}) where T<:Real @ Main.mymod REPL[32]:3
Test Failed at /Users/ferreol/.julia/packages/Aqua/EPo21/src/unbound_args.jl:37
  Expression: isempty(unbounds)
   Evaluated: isempty(Method[foo(x::AbstractArray{<:Union{Missing, T}}) where T<:Real @ Main.mymod REPL[32]:3])

ERROR: There was an error during testing

```

I don’t really understand why as I think I have covered all the cases:

```julia-repl
julia> a = [1.0 missing 2.];

julia> b = ones(3);

julia> c = [missing];

julia> mymod.foo(a)
1-element Vector{Matrix{Float64}}:
 [1.0 0.0 2.0]

julia> mymod.foo(b)
1-element Vector{Vector{Float64}}:
 [1.0, 1.0, 1.0]

julia> mymod.foo(c)
1-element Vector{Vector{Int64}}:
 [0]

```

Any idea about why it is still unbounded?

---

<div class="post-metadata">

**Author:** ![lgoettgens](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/lgoettgens/32/203968_2.png) [@lgoettgens](https://discourse.julialang.org/u/lgoettgens)\
**Post date:** [May 15, 2025, 3:01pm UTC](https://discourse.julialang.org/t/unbound-type-parameters-detected-in-union-missing-t/129042/2 "2025-05-15T15:01:59Z")

</div>

The problem is that the test (which is part of the `Test` stdlib and not `Aqua`) will look at each method individually. So even though the unboundness case will never actually happen due to the second method being defined, it will still be reported.  
This is tracked both in Aqua (as [Unbound argument should not be counted if a method isn't called · Issue #86 · JuliaTesting/Aqua.jl · GitHub](https://github.com/JuliaTesting/Aqua.jl/issues/86)) and in julialang (as [Test.has\_unbound\_vars should show some respect for history · Issue #28086 · JuliaLang/julia · GitHub](https://github.com/JuliaLang/julia/issues/28086)).

---

<div class="post-metadata">

**Author:** ![FerreolS](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/ferreols/32/216854_2.png) [@FerreolS](https://discourse.julialang.org/u/FerreolS)\
**Post date:** [May 15, 2025, 3:17pm UTC](https://discourse.julialang.org/t/unbound-type-parameters-detected-in-union-missing-t/129042/3 "2025-05-15T15:17:22Z")

</div>

Thanks,  
Is there a way to make it bounded in a single method?

---

<div class="post-metadata">

**Author:** ![lgoettgens](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/lgoettgens/32/203968_2.png) [@lgoettgens](https://discourse.julialang.org/u/lgoettgens)\
**Post date:** [May 15, 2025, 3:31pm UTC](https://discourse.julialang.org/t/unbound-type-parameters-detected-in-union-missing-t/129042/4 "2025-05-15T15:31:28Z")

</div>

I think it should be possible by defining three methods with signatures:

```julia
foo(x::AbstractArray{Union{T,Missing}}) where {T <: Real}
foo(x::AbstractArray{T}) where {T <: Real}
foo(x::AbstractArray{Missing})

```

---

<div class="post-metadata">

**Author:** ![FerreolS](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/ferreols/32/216854_2.png) [@FerreolS](https://discourse.julialang.org/u/FerreolS)\
**Post date:** [May 15, 2025, 3:52pm UTC](https://discourse.julialang.org/t/unbound-type-parameters-detected-in-union-missing-t/129042/5 "2025-05-15T15:52:46Z")

</div>

It does not seems so:

```julia-repl
julia> module newmod
       foo(x::AbstractArray{Union{T,Missing}}) where {T <: Real} = ifelse.(.!ismissing.(x) .&& .!isnan.(x), x, T(0))
       foo(x::AbstractArray{T}) where {T <: Real} = ifelse.(.!isnan.(x), x, T(0))
       foo(x::AbstractArray{Missing}) = zeros(size(x))
       end

julia> Aqua.test_unbound_args(newmod)
Unbound type parameters detected:
[1] foo(x::AbstractArray{Union{Missing, T}}) where T<:Real @ Main.newmod REPL[20]:2
Test Failed at /Users/ferreol/.julia/packages/Aqua/EPo21/src/unbound_args.jl:37
  Expression: isempty(unbounds)
   Evaluated: isempty(Method[foo(x::AbstractArray{Union{Missing, T}}) where T<:Real @ Main.newmod REPL[20]:2])

ERROR: There was an error during testing

```

Anyway, I have my answer and I will probably deactivate the test with `broken=true`  
Thanks again

---

<div class="post-metadata">

**Author:** ![nsajko](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/nsajko/32/221187_2.png) [@nsajko](https://discourse.julialang.org/u/nsajko)\
**Post date:** [May 15, 2025, 5:13pm UTC](https://discourse.julialang.org/t/unbound-type-parameters-detected-in-union-missing-t/129042/6 "2025-05-15T17:13:42Z")

</div>

> [@FerreolS](#):
>
> Anyway, I have my answer and I will probably deactivate the test with `broken=true`

That seems like a bad practice though. Do you really need to dispatch on things like this?

---

<div class="post-metadata">

**Author:** ![mbauman](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/mbauman/32/31082_2.png) [@mbauman](https://discourse.julialang.org/u/mbauman)\
**Post date:** [May 15, 2025, 5:24pm UTC](https://discourse.julialang.org/t/unbound-type-parameters-detected-in-union-missing-t/129042/7 "2025-05-15T17:24:42Z")

</div>

Yep, ~~besides just being piracy~~ , I’ve always found dispatching on a `X{<:Union{T, Missing}} where T` quite fraught to use correctly… I’m not even certain how they are working with the rules of Julia’s type system to begin with!

Even more generally, simply dispatching on eltypes can be prone to surprises. It’s not hard to end up with an array whose elements are all some `T` but whose eltype is something wider.

---

<div class="post-metadata">

**Author:** ![FerreolS](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/ferreols/32/216854_2.png) [@FerreolS](https://discourse.julialang.org/u/FerreolS)\
**Post date:** [May 15, 2025, 7:57pm UTC](https://discourse.julialang.org/t/unbound-type-parameters-detected-in-union-missing-t/129042/8 "2025-05-15T19:57:48Z")

</div>

> [@nsajko](#):
>
> That seems like a bad practice though. Do you really need to dispatch on things like this?

I was a bit provocative as I didn’t find a way to properly exclude the function (that is a constructor of a struct in my package).

Actually, I don’t really need those function as I’ve just added it to make my package more composable in the case I’ll register it.

I never use `missing` because it usually ruins the performance:

```julia-repl
julia> N = 1_000
1000

julia> g(x) = sum(x->ifelse(ismissing(x), 0 ,x),x,dims=2)
g (generic function with 1 method)

julia> h(x) = sum(x->ifelse(isnan(x), 0 ,x),x,dims=2)
h (generic function with 1 method)

julia> @btime g($([ifelse(rand().<0.4,missing,randn()) for _ in 1:N, y in 1:N]));
  4.201 ms (3 allocations: 9.08 KiB)

julia> @btime h($([ifelse(rand().<0.4,NaN,randn()) for _ in 1:N, y in 1:N]))
  159.169 μs (3 allocations: 8.08 KiB)

```

---

<div class="post-metadata">

**Author:** ![FerreolS](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/ferreols/32/216854_2.png) [@FerreolS](https://discourse.julialang.org/u/FerreolS)\
**Post date:** [May 15, 2025, 8:08pm UTC](https://discourse.julialang.org/t/unbound-type-parameters-detected-in-union-missing-t/129042/9 "2025-05-15T20:08:27Z")

</div>

> [@mbauman](#):
>
> Yep, besides just being piracy, I’ve always found dispatching on a `X{<:Union{T, Missing}} where T` quite fraught to use correctly… I’m not even certain how they are working with the rules of Julia’s type system to begin with!
> 
> Even more generally, simply dispatching on eltypes can be prone to surprises. It’s not hard to end up with an array whose elements are all some `T` but whose eltype is something wider.

I don’t really understand what do you mean by piracy in this case?

It’s sure that if `T` is not concrete then dispatching on `X{<:Union{T, Missing}}` is a bit useless but not faulty. Out of curiosity, is there a prefered mechanism to `concretize` an array (let say an `Vector{Number}` where every elements are in fact `Float64`), or to dispatch on concrete vs non-concrete `T`

---

<div class="post-metadata">

**Author:** ![mbauman](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/mbauman/32/31082_2.png) [@mbauman](https://discourse.julialang.org/u/mbauman)\
**Post date:** [May 15, 2025, 8:12pm UTC](https://discourse.julialang.org/t/unbound-type-parameters-detected-in-union-missing-t/129042/10 "2025-05-15T20:12:47Z")

</div>

> [@FerreolS](#):
>
> I don’t really understand what do you mean by piracy in this case?

Sorry, you’re right this isn’t piracy as you’re defining your own function.
