# Dispatch using Union and order of arguments

**URL:** <https://discourse.julialang.org/t/dispatch-using-union-and-order-of-arguments/19732>\
**Category:** General Usage\
**Tags:** question\
**Created:** [January 17, 2019, 5:07am UTC](https://discourse.julialang.org/t/dispatch-using-union-and-order-of-arguments/19732 "2019-01-17T05:07:22Z")\
**Posts on this page:** 6\
**Page:** 1

<div class="post-metadata">

**Author:** ![tkf](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/tkf/32/17635_2.png) [@tkf](https://discourse.julialang.org/u/tkf)\
**Post date:** [January 17, 2019, 5:07am UTC](https://discourse.julialang.org/t/dispatch-using-union-and-order-of-arguments/19732/1 "2019-01-17T05:07:22Z")

</div>

I tried to subtract a type `T` from a union type with this function (a “poor man’s `Core.Compiler.typesubtract`”):

```julia
typesubtract(::Type{Union{T,S}}, ::Type{T}) where {S, T} = S
typesubtract(::Type{S}, ::Type{T}) where {S, T} = S
typesubtract(::Type{T}, ::Type{T}) where {T} = Union{}

```

However, this function did not work for simple case like `typesubtract(Union{Missing, Float64, Int}, Missing)` which returns `Union{Missing, Float64, Int64}` while I expected `Union{Float64, Int}`.

On the other hand, just flipping arguments as the function `union_poptype` works (This is taken from @galenlynch’s [PR #30549](https://github.com/JuliaLang/julia/pull/30549)):

```julia
union_poptype(::Type{T}, ::Type{Union{T,S}}) where {T, S} = S
union_poptype(::Type{T}, ::Type{S}) where {T, S} = S
union_poptype(::Type{T}, ::Type{T}) where {T} = Union{}

```

Why `union_poptype` works and my `typesubtract` doesn’t? Does Julia’s dispatch system depend on order of arguments? I thought the type system prefers to throw ambiguity error rather than using argument order to break ties.

* * *

I checked those functions using

```julia
using Test

@testset "union_poptype" begin
    @test union_poptype(Missing, Union{Missing, Float64, Int}) == Union{Float64, Int}
    @test union_poptype(Missing, Union{Missing, Float64}) == Float64
    @test union_poptype(Missing, Union{Float64, Int}) == Union{Float64, Int}
    @test union_poptype(Missing, Float64) == Float64
end

@testset "typesubtract" begin
    @test typesubtract(Union{Missing, Float64, Int}, Missing) == Union{Float64, Int}
    @test typesubtract(Union{Missing, Float64}, Missing) == Float64
    @test typesubtract(Union{Float64, Int}, Missing) == Union{Float64, Int}
    @test typesubtract(Float64, Missing) == Float64
end

```

which yields

```julia
Test Summary: | Pass Total
union_poptype | 4 4
typesubtract: Test Failed at ...
  Expression: typesubtract(Union{Missing, Float64, Int}, Missing) == Union{Float64, Int}
   Evaluated: Union{Missing, Float64, Int64} == Union{Float64, Int64}
  ...
typesubtract: Test Failed at ...
  Expression: typesubtract(Union{Missing, Float64}, Missing) == Float64
   Evaluated: Union{Missing, Float64} == Float64
Stacktrace:
  ...
Test Summary: | Pass Fail Total
typesubtract | 2 2 4

```

---

<div class="post-metadata">

**Author:** ![rdeits](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/rdeits/32/286_2.png) [@rdeits](https://discourse.julialang.org/u/rdeits)\
**Post date:** [January 17, 2019, 12:18pm UTC](https://discourse.julialang.org/t/dispatch-using-union-and-order-of-arguments/19732/2 "2019-01-17T12:18:06Z")

</div>

Does reversing the order of T and S inside the `where {T, S}` make any difference?

---

<div class="post-metadata">

**Author:** ![tkf](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/tkf/32/17635_2.png) [@tkf](https://discourse.julialang.org/u/tkf)\
**Post date:** [January 17, 2019, 4:44pm UTC](https://discourse.julialang.org/t/dispatch-using-union-and-order-of-arguments/19732/3 "2019-01-17T16:44:10Z")

</div>

No difference. Flipping the type parameters in `where` of `typesubtract` and `union_poptype` doesn’t change the result.

---

<div class="post-metadata">

**Author:** ![Tamas\_Papp](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/tamas_papp/32/25949_2.png) [@Tamas\_Papp](https://discourse.julialang.org/u/Tamas_Papp)\
**Post date:** [January 17, 2019, 5:05pm UTC](https://discourse.julialang.org/t/dispatch-using-union-and-order-of-arguments/19732/4 "2019-01-17T17:05:47Z")

</div>

> [@tkf](#):
>
> Does Julia’s dispatch system depend on order of arguments?

I think in this case

```julia
typesubtract(::Type{Union{T,S}}, ::Type{T}) where {S, T} = S

```

is not matched for the call

```julia
typesubtract(Union{Missing, Float64, Int}, Missing)

```

You can check this by defining just this method. I suspect this is a bug, but I am an expert on types and dispatch, hopefully someone will clarify this.

Also, I can make it match with `:>`, but

```julia
julia> typesubtract(::Type{>: Union{T,S}}, ::Type{T}) where {S,T} = S
typesubtract (generic function with 1 method)

julia> typesubtract(Union{Missing, Float64, Int}, Missing)
ERROR: UndefVarError: S not defined
Stacktrace:
 [1] typesubtract(::Type{Union{Missing, Float64, Int64}}, ::Type{Missing}) at ./REPL[1]:1
 [2] top-level scope at REPL[2]:1

```

looks like a bug.

---

<div class="post-metadata">

**Author:** ![tkf](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/tkf/32/17635_2.png) [@tkf](https://discourse.julialang.org/u/tkf)\
**Post date:** [January 17, 2019, 5:31pm UTC](https://discourse.julialang.org/t/dispatch-using-union-and-order-of-arguments/19732/5 "2019-01-17T17:31:47Z")

</div>

> [@Tamas\_Papp](#):
>
> You can check this by defining just this method.

Ah, that’s a clever trick. And you are right. The method does not match:

```julia
julia> f(::Type{Union{T,S}}, ::Type{T}) where {S, T} = S
f (generic function with 1 method)

julia> f(Union{Missing, Float64, Int}, Missing)
ERROR: MethodError: no method matching f(::Type{Union{Missing, Float64, Int64}}, ::Type{Missing})
Closest candidates are:
  f(::Type{Union{T, S}}, ::Type{T}) where {S, T} at REPL[2]:1
Stacktrace:
 [1] top-level scope at REPL[3]:1

julia> g(::Type{T}, ::Type{Union{T,S}}) where {S, T} = S
g (generic function with 1 method)

julia> g(Missing, Union{Missing, Float64, Int})
Union{Float64, Int64}

```

> [@Tamas\_Papp](#):
>
> Also, I can make it match with `:>` , but

This is nice. I didn’t realize `:>` can be used in this case.

But it looks like `S not defined` part is a known issue (or in a way “by design”). Jeff Bezanson says:

> There are various cases where we can’t determine the value of a static parameter, and we decided it was better to give errors in those cases than to affect method matching  
> — [lower bound interferes with typevar value inference · Issue #30713 · JuliaLang/julia · GitHub](https://github.com/JuliaLang/julia/issues/30713#issuecomment-454129552)

---

<div class="post-metadata">

**Author:** ![tkf](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/tkf/32/17635_2.png) [@tkf](https://discourse.julialang.org/u/tkf)\
**Post date:** [January 18, 2019, 4:55am UTC](https://discourse.julialang.org/t/dispatch-using-union-and-order-of-arguments/19732/6 "2019-01-18T04:55:38Z")

</div>

I opened the issue here: [Argument order dependency in dispatching on Type{Union{T,S}} and Type{T} · Issue #30751 · JuliaLang/julia · GitHub](https://github.com/JuliaLang/julia/issues/30751)

BTW, I noticed that it works in Julia 0.6.4

```julia
julia> f(::Type{Union{T,S}}, ::Type{T}) where {S, T} = S
f (generic function with 1 method)

julia> f(Union{Void, Float64, Int}, Void)
Union{Float64, Int64}

julia> g(::Type{T}, ::Type{Union{T,S}}) where {S, T} = S
g (generic function with 1 method)

julia> g(Void, Union{Void, Float64, Int})
Union{Float64, Int64}

```
