# Function definition inside let-block impacting performance?

**URL:** <https://discourse.julialang.org/t/function-definition-inside-let-block-impacting-performance/110007>\
**Category:** Performance\
**Tags:** question\
**Created:** [February 9, 2024, 8:06pm UTC](https://discourse.julialang.org/t/function-definition-inside-let-block-impacting-performance/110007 "2024-02-09T20:06:28Z")\
**Posts on this page:** 3\
**Page:** 3

<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:** [February 13, 2024, 8:16pm UTC](https://discourse.julialang.org/t/function-definition-inside-let-block-impacting-performance/110007/41 "2024-02-13T20:16:35Z")

</div>

> [@mnemnion](#):
>
> there is one valid return type

If you mean along with possible runtime `typeassert` errors, then yes, concrete argument annotations can box 1 method into 1 possible specialization with 1 possible return type.

> [@mnemnion](#):
>
> A call to `a_fn` doesn’t need boxing.

You didn’t get my point. Boxing in this case occurred for captured variables that were not yet assigned to a 2nd closure when the 1st closure was instantiated; in the `fib` case the 1st and 2nd closure are the same. To oversimplify the thread, uniment suggested inserting the 2nd closure’s lifted type into a typed box, while I suggested instantiation of the 2nd closure from the lifted type in the 1st closure’s lifted method, possibly both ways. That is all predicated on the closure name being assigned once ever, like a function in the global scope. The more common reason for boxing is the variable being reassigned (like `foo() = 1` then `foo = 2`), and inference is fundamentally limited there; the upper limit is the lowerer proving all reassignments’ right hand sides never involve unpredictable closure variables and letting the compiler infer the type, otherwise it must be `::Any`. In short, boxing occurs over captured variable assignments, not whether the function instance itself has multiple methods, so a unimethod would not help and would be boxed for the same reasons.

---

<div class="post-metadata">

**Author:** ![uniment](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/uniment/32/24532_2.png) [@uniment](https://discourse.julialang.org/u/uniment)\
**Post date:** [February 14, 2024, 1:26am UTC](https://discourse.julialang.org/t/function-definition-inside-let-block-impacting-performance/110007/42 "2024-02-14T01:26:32Z")

</div>

Maybe it will simplify this discussion to show that functions don’t usually need to be boxed, even if they are locally declared, have multiple methods, and are captured:

```julia
julia> fun = let
           f(x)=x
           f(x,y)=x+y
           g=f # new local id needed because lowering capture logic needs work
           (args...) -> g(args...)
       end
#3 (generic function with 1 method)

julia> fun.g # no box
(::var"#f#4") (generic function with 2 methods)

julia> using BenchmarkTools
       @btime($fun($1)), @btime($fun($2,$3))
  1.800 ns (0 allocations: 0 bytes)
  1.800 ns (0 allocations: 0 bytes)
(1, 5)

```

---

<div class="post-metadata">

**Author:** ![uniment](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/uniment/32/24532_2.png) [@uniment](https://discourse.julialang.org/u/uniment)\
**Post date:** [February 14, 2024, 10:18pm UTC](https://discourse.julialang.org/t/function-definition-inside-let-block-impacting-performance/110007/43 "2024-02-14T22:18:36Z")

</div>

> [@uniment](#):
>
> `# new local id needed because lowering capture logic needs work`

Issue opened:

[Unnecessary Box of Multi-Method Local Function · Issue #53338 · JuliaLang/julia (github.com)](https://github.com/JuliaLang/julia/issues/53338)

[Previous page](https://discourse.julialang.org/t/function-definition-inside-let-block-impacting-performance/110007.md?page=2)
