# Type stability with variable arguments

**URL:** <https://discourse.julialang.org/t/type-stability-with-variable-arguments/74640>\
**Category:** Performance\
**Tags:** type-stability\
**Created:** [January 14, 2022, 7:22pm UTC](https://discourse.julialang.org/t/type-stability-with-variable-arguments/74640 "2022-01-14T19:22:44Z")\
**Posts on this page:** 1\
**Showing post:** 12

<div class="post-metadata">

**Author:** ![stevengj](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/stevengj/32/71_2.png) [@stevengj](https://discourse.julialang.org/u/stevengj)\
**Post date:** [January 15, 2022, 4:44pm UTC](https://discourse.julialang.org/t/type-stability-with-variable-arguments/74640/12 "2022-01-15T16:44:00Z")

</div>

> [@Henrique\_Becker](#):
>
> In your new example the types of the `lexicographic2` parameters is also not known at parse time, only at call time.

That’s not how type inference works in Julia. The key thing is what happens at _compile-time_, not at parse time (the parser only knows how things are _spelled_).

It’s totally fine to declare `lexicographic2(a,b,c)` with no types for the arguments — the compiler knows where the function is _called_, and (assuming the call site is [type-stable/grounded/inferable](https://discourse.julialang.org/t/what-is-grounded-type-in-julia/74579)) it compile a specialized version of `lexicographic2` (and hence a specialized version of the returned function `lt`) for those argument types.

The problem with `lexicographic2` is that the local variable `v` is type-unstable — it takes on 3 different types because `(a,b,c)` has 3 different types. If instead you write out the loop:

```julia
       function lexicographic3(a,b,c )
           function lt( x::Int, y::Int )
               if a[x] < a[y]
                   return true
               elseif a[x] > a[y]
                   return false
               elseif b[x] < b[y]
                   return true
               elseif b[x] > b[y]
                   return false
               elseif c[x] < c[y]
                   return true
               elseif c[x] > c[y]
                   return false
               else
                   return false
               end
           end
           return lt
       end

```

then the type-instability of `v` goes away and the performance is good:

```julia
julia> @btime sort!( $indices, lt=$(lexicographic3( x, y, z )));
  48.989 ms (0 allocations: 0 bytes)

```

(Note the lack of allocations — if you have any allocations here, that is indicative of a type-instability.)

The trick is to get the compiler to unroll the loop for you, for any number of arguments. I usually use `map` or `ntuple` for this, since it is specialized for tuples, and it is fine to construct a tuple result that is discarded (the compiler will eliminate the tuple allocation). I’d rather use `foreach`, but that is currently not specialized for tuples ([julia#31901](https://github.com/JuliaLang/julia/pull/31901)):

```julia
function lexicographic4(args...) # no type declarations needed here: the compiler will figure it out
    function lt(x, y) # similarly, no type-declarations needed here either
        map(args) do v # the compiler will completely unroll a map of a tuple
            if v[x] < v[y]
                return true
            elseif v[x] > v[y]
                return false
            end
        end # map result (a tuple) is discarded … compiler will elide it
        return false
    end
    return lt
end

```

which gives:

```julia
julia> @btime sort!( $indices, lt=$(lexicographic4( x, y, z )));
  44.779 ms (0 allocations: 0 bytes)

```

as desired.

---

_[View the full topic](https://discourse.julialang.org/t/type-stability-with-variable-arguments/74640)._
