# Define multiple methods or one method with union types?

**URL:** <https://discourse.julialang.org/t/define-multiple-methods-or-one-method-with-union-types/101674>\
**Category:** Performance\
**Tags:** multidispatch, varargs\
**Created:** [July 16, 2023, 2:08pm UTC](https://discourse.julialang.org/t/define-multiple-methods-or-one-method-with-union-types/101674 "2023-07-16T14:08:07Z")\
**Posts on this page:** 5\
**Page:** 1

<div class="post-metadata">

**Author:** ![kunzaatko](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/kunzaatko/32/25153_2.png) [@kunzaatko](https://discourse.julialang.org/u/kunzaatko)\
**Post date:** [July 16, 2023, 2:08pm UTC](https://discourse.julialang.org/t/define-multiple-methods-or-one-method-with-union-types/101674/1 "2023-07-16T14:08:07Z")

</div>

What is the preferred way to implement methods that allow multiple types?

Option **A** :

```julia
function method(a::Union{Tuple{Integer,Integer}, Integer}, b::Union{Tuple{Integer,Integer}, Integer})
    a = a isa Integer ? (a,a) : a
    b = b isa Integer ? (b,b) : b
    # ...
end

```

or option **B** :

```julia
method(a::Integer, args...) = method((a,a), args...)
method(a::Tuple{Integer, Integer}, b::Integer) = method(a, (b,b))
function method(a::Tuple{Integer, Integer}, b::Tuple{Integer, Integer})
    #...
end

```

Is the conversion compiled away in option **A** or does it introduce a performance hit?

_NOTE:_ This is only an example and I know that in this case, the difference in running time would in fact be negligible. I use it because it is illustratory. Assume that the two conversions in option **A** are expensive.

---

<div class="post-metadata">

**Author:** ![gdalle](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/gdalle/32/27854_2.png) [@gdalle](https://discourse.julialang.org/u/gdalle)\
**Post date:** [July 16, 2023, 5:31pm UTC](https://discourse.julialang.org/t/define-multiple-methods-or-one-method-with-union-types/101674/2 "2023-07-16T17:31:05Z")

</div>

In this case option B is definitely better.  
It is much more Julian in spirit because it relies on [multiple dispatch](https://docs.julialang.org/en/v1/manual/methods/), which is basically the same as testing `a isa ...` but happens during compilation.  
And as a side bonus, it allows you to give the methods different docstrings.  
I’m gonna take a look at performance cause I’m not 100% sure, but I think there is no case in which option A is faster.

EDIT: I did take a look at performance, and at first glance both versions generate the same LLVM code. So the compiler is smart enough to not be disturbed by the type checking. However, in terms of style and clarity option B remains the clear winner.

---

<div class="post-metadata">

**Author:** ![mkitti](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/mkitti/32/12459_2.png) [@mkitti](https://discourse.julialang.org/u/mkitti)\
**Post date:** [July 16, 2023, 6:53pm UTC](https://discourse.julialang.org/t/define-multiple-methods-or-one-method-with-union-types/101674/3 "2023-07-16T18:53:39Z")

</div>

You might want to consider this pattern.

```julia
julia> module OptionC
           @inline _to_integer_integer(x::Integer) = (x,x)
           @inline _to_integer_integer(x::Tuple{Integer,Integer}) = x
           method(a,b) = method(
               _to_integer_integer(a),
               _to_integer_integer(b)
           )
           function method(
               a::Tuple{Integer,Integer},
               b::Tuple{Integer,Integer}
           )
               # ...
               return a,b
           end
       end

```

You could then extend the functionality by overloading `_to_integer_integer`:

```julia
julia> OptionC._to_integer_integer(x::AbstractArray{<:Integer,N} where N) =
           length(x) in (1,2) ? (first(x),last(x)) :
               throw(ArgumentError("Arrays must be of length 1 or 2"))

julia> OptionC.method([1],[2])
((1, 1), (2, 2))

julia> OptionC.method([1],[2,3])
((1, 1), (2, 3))

julia> OptionC.method(1:2,3:4)
((1, 2), (3, 4))

julia> OptionC._to_integer_integer(x::Pair{<:Integer, <:Integer}) =
           (first(x),last(x))

julia> OptionC.method(1 => 2, 3 => 4)
((1, 2), (3, 4))

```

---

<div class="post-metadata">

**Author:** ![kunzaatko](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/kunzaatko/32/25153_2.png) [@kunzaatko](https://discourse.julialang.org/u/kunzaatko)\
**Post date:** [July 17, 2023, 8:11am UTC](https://discourse.julialang.org/t/define-multiple-methods-or-one-method-with-union-types/101674/4 "2023-07-17T08:11:47Z")

</div>

Thank you!  
As a follow-up, since method **B** may require to define a lot of methods, is there a way of avoiding the use of both optional arguments and normal arguments swallowing and splatting in the definitions of the methods. An example:

```julia
method(a, b::Integer, args...) = method(a, (b,b), args...)

```

must be written as

```julia
method(a, b::Integer, args...; varargs...) = method(a, (b,b), args...; varargs...)

```

if it has any optional arguments to it. Is there any idiomatic way or clever syntax of avoiding doing this? I would like to avoid defining a macro, since it would make the code non-transparent (in my opinion).

---

<div class="post-metadata">

**Author:** ![gdalle](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/gdalle/32/27854_2.png) [@gdalle](https://discourse.julialang.org/u/gdalle)\
**Post date:** [July 17, 2023, 2:03pm UTC](https://discourse.julialang.org/t/define-multiple-methods-or-one-method-with-union-types/101674/5 "2023-07-17T14:03:17Z")

</div>

I think this is a typical use case for automated code generation with `@eval`. This is different from a macro because it’s something you do only once as a package developer (instead of leaving it out there for package users), and also because it looks much simpler (you don’t need to manipulate an abstract syntax tree).

This trick is used a lot even in Base Julia: i did a quick search, and i think examples like [this one with `Missing`](https://github.com/JuliaLang/julia/blob/7141e73016969562817298a50d7fac3d5c0b2f98/base/missing.jl#L94-L102) are pretty typical.

Side note: @oxinabox wrote a blog post about this exact topic yesterday, I haven’t yet had time to check it out but you may find it interesting. [Resident Eval, Top level metaprogramming patterns in JuliaLang](https://www.oxinabox.net/2023/06/17/resident-eval.html)
