# Variable number of arguments with different types

**URL:** <https://discourse.julialang.org/t/variable-number-of-arguments-with-different-types/102035>\
**Category:** General Usage\
**Tags:** varargs\
**Created:** [July 24, 2023, 7:29pm UTC](https://discourse.julialang.org/t/variable-number-of-arguments-with-different-types/102035 "2023-07-24T19:29:34Z")\
**Posts on this page:** 9\
**Page:** 1

<div class="post-metadata">

**Author:** ![linwaytin](https://avatars.discourse-cdn.com/v4/letter/l/898d66/32.png) [@linwaytin](https://discourse.julialang.org/u/linwaytin)\
**Post date:** [July 24, 2023, 7:29pm UTC](https://discourse.julialang.org/t/variable-number-of-arguments-with-different-types/102035/1 "2023-07-24T19:29:34Z")

</div>

This might be a strange question, but I really want to know if there is a way to construct a function where the first few variables are Int, while the rest are Float64, so that I can call it using like `fn(1, 2, 3.2, 3.4, 4.1)`, with the numbers of Int and Float64 are parametric.

I tried things like

```julia
function f(v::Vararg{Int, 2}, w::Vararg{Float64, 3})
end

```

which does not work.

Thanks.

---

<div class="post-metadata">

**Author:** ![HanD](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/hand/32/213908_2.png) [@HanD](https://discourse.julialang.org/u/HanD)\
**Post date:** [July 24, 2023, 8:00pm UTC](https://discourse.julialang.org/t/variable-number-of-arguments-with-different-types/102035/2 "2023-07-24T20:00:36Z")

</div>

Probably not. Only the last argument can be variadic. But you can wrap both groups in a tuple:

```julia
function f(v::NTuple{2, Int}, w::NTuple{3, Float64})
end

```

---

<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:** [July 24, 2023, 11:16pm UTC](https://discourse.julialang.org/t/variable-number-of-arguments-with-different-types/102035/3 "2023-07-24T23:16:14Z")

</div>

At first glance it seems reasonable to allow nontrailing fixed-length `Vararg` so that a sequence of arguments is automatically packed into tuples in the method body, but this creates a new source of ambiguity in multimethods:

```julia
# Pretend this doesn't error
function f(v::Vararg{Int, 2}, w::Int); (v, w) end
function f(v::Int, w::Vararg{Int, 2}); (v, w) end

f(1, 2, 3) # which method and result?

```

Method ambiguities are already possible due to multiple dispatch involving abstract types, but at least those can be resolved by adding a method with more specific argument types to cover the ambiguous cases. There’s no way to do that for this example, you must sacrifice a method and there’s no reasonable criterion besides recency. There’s no ambiguity if we write out the number of arguments and manually pack the tuples:

```julia
function f(v::Int, v2::Int, w::Int); ((v, v2), w) end

```

---

<div class="post-metadata">

**Author:** ![Sevi](https://avatars.discourse-cdn.com/v4/letter/s/c67d28/32.png) [@Sevi](https://discourse.julialang.org/u/Sevi)\
**Post date:** [July 25, 2023, 6:58am UTC](https://discourse.julialang.org/t/variable-number-of-arguments-with-different-types/102035/4 "2023-07-25T06:58:50Z")

</div>

Sounds like an easy solution might be to separate `f` into a function that reads the integers and floats and the “internal” function, something like

```julia
function f(numbers...)
    ints = filter(n -> n isa Int, numbers)
    floats = filter(n -> n isa Float64, numbers)

    _f(ints, floats)
end

function _f(ints, floats)
    # ...
end

```

Note that this version would also recognize integers/floats that appear out of order. And it would ignore any elements of `numbers` that are neither `Int` nor `Float64`.

This separation you can also do before you call the function, so I’m wondering about the reason you need the inputs all splatted in the function call? It seems more intuitive to just keep them separate (like `w` and `v` in your example).

---

<div class="post-metadata">

**Author:** ![linwaytin](https://avatars.discourse-cdn.com/v4/letter/l/898d66/32.png) [@linwaytin](https://discourse.julialang.org/u/linwaytin)\
**Post date:** [July 25, 2023, 4:23pm UTC](https://discourse.julialang.org/t/variable-number-of-arguments-with-different-types/102035/5 "2023-07-25T16:23:57Z")

</div>

This approach seems to work. Is the code type-stable?  
I tried `function f(numbers...::Tuple{Int, Float64})` but it is not allowed.

Sometimes a math function has both integer and real number input, so I’m just curious if there is a simple way to define functions like that.

---

<div class="post-metadata">

**Author:** ![linwaytin](https://avatars.discourse-cdn.com/v4/letter/l/898d66/32.png) [@linwaytin](https://discourse.julialang.org/u/linwaytin)\
**Post date:** [July 25, 2023, 4:29pm UTC](https://discourse.julialang.org/t/variable-number-of-arguments-with-different-types/102035/6 "2023-07-25T16:29:18Z")

</div>

Thanks for the explanation. So it looks like there is no fundamental way to do this.

---

<div class="post-metadata">

**Author:** ![Sevi](https://avatars.discourse-cdn.com/v4/letter/s/c67d28/32.png) [@Sevi](https://discourse.julialang.org/u/Sevi)\
**Post date:** [July 25, 2023, 7:38pm UTC](https://discourse.julialang.org/t/variable-number-of-arguments-with-different-types/102035/7 "2023-07-25T19:38:15Z")

</div>

> [@linwaytin](#):
>
> This approach seems to work. Is the code type-stable?

The compiler should be able to tell that the resulting `ints` and `floats` are tuples of the respective types in this example. If you check the output of, for instance, `@code_warntype f(1, 3, 2.5, 1.0)` you can see what Julia is able to tell about the types of the variables inside the function. Whether the whole function is type-stable depends on `_f` of course.

Note that once a function is called, the types of the inputs are known and a method will be compiled for this particular type signature. E.g. here when `_f(integers, floats)` is called, the compiler will know that `integers` is some tuple of a specific size containing integers etc. It can then compile a particular (fast) method of `_f` that deals with these inputs.

In my experience, there is generally no need (or performance benefit) to annotate the types of function arguments, unless you want to use multiple dispatch and define/extend different methods which are specialized for certain input types. If I don’t need to restrict the types for a specific reason, I tend not to do it. But it might also increase readability sometimes to clarify what objects are “intended” to be used with this function.

> [@linwaytin](#):
>
> I tried `function f(numbers...::Tuple{Int, Float64})` but it is not allowed.

A `Tuple{Int, Float64}` is a tuple of exactly two elements (the first one of type `Int` and the second of type `Float64`. So something like `(1, 2.0)`, but if there are more elements, like in `(1, 2, 3)` this would be a `Tuple{Int, Int, Int}` and so on.

There is the `NTuple` type that @HanD mentioned, which can simplify the notation a little bit.

In your example, I think two things are important to point out:

- You _can_ put a type restriction on a slurped argument (using the three dots `...`), but it has to come before the three dots:  
`f(numbers::Tuple{Int,Float64}...)` would work

- But this is just fixing the syntax and I think it still does not do what you want. This example `f(numbers::Tuple{Int, Float64}...)` will accept any number of “tuples of exactly two elements, the first being an int and the second a float”. So

These kind of restrictions are exactly why I feel like omitting type annotations is the better choice more often than not. In different projects I started out restricting the input types over-eagerly, only to realize that I later want the function to handle `Float64` as well as `Float32` and `Int`, etc. Just leaving the type annotation away is the solution in many of these situations.

> [@linwaytin](#):
>
> Sometimes a math function has both integer and real number input, so I’m just curious if there is a simple way to define functions like that.

There are few functions in math that absolutely need an integer. Most are defined for (some subset of) the real numbers, i.e. work with floats. Julia (and many other programming languages) will “promote” the numeric types according to some rules such that the computer can work out the same result (up to small errors from rounding, etc.), regardless if you compute `(3.14 ^ 2)`, or `(3.14 ^ 2.0)`, or `(3.14 * 3.14)`. All the basic operations like `+`, `*`, `/` etc. are already defined in the proper way for all possible `Number` inputs, so you usually don’t need to worry about multiplying numbers of different types. The result will be of the type that “makes most sense”.

In the case you really do need integers (e.g. when using indexing), you can just manually convert the inputs by using `Int(x)` in the appropriate place.

More background about conversion and specifically promotion can also be found here  
[https://docs.julialang.org/en/v1/manual/conversion-and-promotion/](https://docs.julialang.org/en/v1/manual/conversion-and-promotion/)

* * *

Sorry for the long-winded answer and if I mention stuff you already knew (very likely) 😅 But I had the impression that you might be running into an [XY Problem](https://xyproblem.info/) down the road, since it’s still not clear to me in what situation you would actually _need_ a function like you originally proposed.

---

<div class="post-metadata">

**Author:** ![algunion](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/algunion/32/51630_2.png) [@algunion](https://discourse.julialang.org/u/algunion)\
**Post date:** [July 25, 2023, 7:50pm UTC](https://discourse.julialang.org/t/variable-number-of-arguments-with-different-types/102035/8 "2023-07-25T19:50:16Z")

</div>

> [@linwaytin](#):
>
> I tried `function f(numbers...::Tuple{Int, Float64})` but it is not allowed.

I think you are looking for something like this instead of the `Tuple` usage:

```julia
function f(numbers::Union{Int,Float64}...)
    @info numbers
end

f(1, 2, 3.3, 4.4, 5.5)

```

---

<div class="post-metadata">

**Author:** ![linwaytin](https://avatars.discourse-cdn.com/v4/letter/l/898d66/32.png) [@linwaytin](https://discourse.julialang.org/u/linwaytin)\
**Post date:** [July 25, 2023, 9:36pm UTC](https://discourse.julialang.org/t/variable-number-of-arguments-with-different-types/102035/9 "2023-07-25T21:36:10Z")

</div>

@Sevi Thanks for such a detailed explanation. I think then `f(numbers...)` is enough for efficient code. This is quite surprising but maybe it is the natural result of julia’s design.

@algunion Thanks. Indeed, this is pretty much the best we can do.

What I want originally is a generic way to specify the type of each variable in the way like parametric types. Using `f(numbers::Union{Int, Float64}...)` with some conditions in the function body before real evaluation is probably good enough.

Thanks all for answering this strange question.
