# Vararg (sub)type parameter undocumented dispatch weirdness

**URL:** <https://discourse.julialang.org/t/vararg-sub-type-parameter-undocumented-dispatch-weirdness/87110>\
**Category:** General Usage\
**Tags:** type, parametric-types, dispatch, ntuple\
**Created:** [September 12, 2022, 6:13am UTC](https://discourse.julialang.org/t/vararg-sub-type-parameter-undocumented-dispatch-weirdness/87110 "2022-09-12T06:13:46Z")\
**Posts on this page:** 8\
**Page:** 1

<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:** [September 12, 2022, 6:13am UTC](https://discourse.julialang.org/t/vararg-sub-type-parameter-undocumented-dispatch-weirdness/87110/1 "2022-09-12T06:13:46Z")

</div>

```julia
a1(::Integer...) = nothing
a2(::I...) where {I <: Integer} = nothing
a3(::Vararg{Integer}) = nothing
a4(::Vararg{<:Integer}) = nothing
a5(::Vararg{I}) where {I <: Integer} = nothing
b3(::NTuple{n, Integer}) where {n} = nothing
b4(::NTuple{n, <:Integer}) where {n} = nothing
b5(::NTuple{n, I}) where {n, I <: Integer} = nothing

# Intended for calling the above with heterogeneous arguments
a(f) = f(zero(UInt8), zero(Int8))
b(f) = f(a(tuple))

a(a1)
a(a2) # throws MethodError
a(a3)
a(a4) # unexpectedly succeeds
a(a5) # throws MethodError
b(b3)
b(b4) # throws MethodError
b(b5) # throws MethodError

```

Why does `a(a4)` succeed? In particular, how is it different from `a(a5)` and from `b(b4)`?

---

<div class="post-metadata">

**Author:** ![jishnub](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/jishnub/32/33620_2.png) [@jishnub](https://discourse.julialang.org/u/jishnub)\
**Post date:** [September 12, 2022, 7:11am UTC](https://discourse.julialang.org/t/vararg-sub-type-parameter-undocumented-dispatch-weirdness/87110/2 "2022-09-12T07:11:27Z")

</div>

`a4` requires the arguments to be `Integer`s. `a5` requires all arguments to be of the same type `I`, which in turn is a subtype of `Integer`.

---

<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:** [September 12, 2022, 9:38am UTC](https://discourse.julialang.org/t/vararg-sub-type-parameter-undocumented-dispatch-weirdness/87110/3 "2022-09-12T09:38:10Z")

</div>

So `a4` behaves the same as `a3` and `b3`.  
But why does it not behave the same as `b4`, in both `a4` and `b4` we have `<:T` used in the place of the type parameter?

---

<div class="post-metadata">

**Author:** ![Tharsalys](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/tharsalys/32/38414_2.png) [@Tharsalys](https://discourse.julialang.org/u/Tharsalys)\
**Post date:** [September 12, 2022, 10:42am UTC](https://discourse.julialang.org/t/vararg-sub-type-parameter-undocumented-dispatch-weirdness/87110/4 "2022-09-12T10:42:21Z")

</div>

I think the answer is here:

```julia
julia> methods(b4)
# 1 method for generic function "b4":
[1] b4(::Tuple{Vararg{var"#s6", n}} where var"#s6"<:Integer) where n in Main at REPL[15]:1

```

The compiler internally assigns a name to the integer part in `b4`’s signature. So it ends up being the same as `a2` where the integer has to be the same concrete type.

---

<div class="post-metadata">

**Author:** ![lnguyen](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/lnguyen/32/42613_2.png) [@lnguyen](https://discourse.julialang.org/u/lnguyen)\
**Post date:** [September 12, 2022, 2:04pm UTC](https://discourse.julialang.org/t/vararg-sub-type-parameter-undocumented-dispatch-weirdness/87110/5 "2022-09-12T14:04:01Z")

</div>

The difference is subtle: `a4` matches argument tuples of the type `Tuple{Vararg{T} where T <: Integer}`, while `a5` narrows to `Tuple{Vararg{T}} where T <: Integer`. When called on two or more arguments, the latter is a [diagonal type](https://docs.julialang.org/en/v1/devdocs/types/#Diagonal-types) that restricts T to range over only concrete types, hence the `MethodError`.

---

<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:** [September 12, 2022, 3:34pm UTC](https://discourse.julialang.org/t/vararg-sub-type-parameter-undocumented-dispatch-weirdness/87110/6 "2022-09-12T15:34:23Z")

</div>

OK, so it seems that I can call `methods(my_function)` to find out how the dispatch will behave for my function.

But does some consistent set of rules exist that I could learn to be able to predict this myself, because I’d like to avoid calling `methods` all the time?

EDIT: no, actually `methods` doesn’t even help me much, because it tells you almost nothing about `a4`, unlike with `b4`. And `a4` is the surprising one for me.

---

<div class="post-metadata">

**Author:** ![lnguyen](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/lnguyen/32/42613_2.png) [@lnguyen](https://discourse.julialang.org/u/lnguyen)\
**Post date:** [September 12, 2022, 6:24pm UTC](https://discourse.julialang.org/t/vararg-sub-type-parameter-undocumented-dispatch-weirdness/87110/7 "2022-09-12T18:24:35Z")

</div>

The easiest way to test dispatch behavior is with subtyping relations. Julia’s dispatch rule is very simple and consistent in this respect: the method selected has the most specific type signature that your argument is a subtype of. For example:

```julia
julia> x = zero(UInt8), zero(Int8)
(0x00, 0)

julia> typeof(x) <: Tuple{Vararg{<:Integer}} # a4's method signature
true

julia> typeof(x) <: Tuple{Vararg{T}} where T<:Integer # a5's method signature
false

```

The [developer documentation](https://docs.julialang.org/en/v1/devdocs/types/#More-about-types) is a good reference for Julia’s subtyping rules. For a deeper dive, [this paper](https://dl.acm.org/doi/pdf/10.1145/3276483) is a classic.

---

<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:** [January 29, 2023, 12:02am UTC](https://discourse.julialang.org/t/vararg-sub-type-parameter-undocumented-dispatch-weirdness/87110/8 "2023-01-29T00:02:59Z")

</div>

Turns out that the _mysterious_ `a4` was actually declared with a deprecated syntax, as shown by starting Julia with `--depwarn=error`:

```plaintext
$ julia --depwarn=error
               _
   _ _ _(_)_ | Documentation: https://docs.julialang.org
  (_) | (_) (_) |
   _ _ _| |_ __ _ | Type "?" for help, "]?" for Pkg help.
  | | | | | | |/ _` | |
  | | |_| | | | (_| | | Version 1.10.0-DEV.453 (2023-01-28)
 _/ |\ __'_|_|_|\__'_| | Commit 7d4309c9c31 (0 days old master)
|__/ |

julia> a4(::Vararg{<:Integer}) = nothing
ERROR: Wrapping `Vararg` directly in UnionAll is deprecated (wrap the tuple instead).
Stacktrace:
 [1] UnionAll(v::TypeVar, t::Any)
   @ Core ./boot.jl:257

```

I guess this revelation makes my questions from a few months ago moot, although it’s really too bad that Julia has to be started with a special flag to tell you this information.
