# RFC: Some Ideas to Tackle #15276 - performance of captured variables in closures

**URL:** <https://discourse.julialang.org/t/rfc-some-ideas-to-tackle-15276-performance-of-captured-variables-in-closures/95260>\
**Category:** Internals & Design\
**Tags:** inference, type-stability, corebox\
**Created:** [February 27, 2023, 10:09am UTC](https://discourse.julialang.org/t/rfc-some-ideas-to-tackle-15276-performance-of-captured-variables-in-closures/95260 "2023-02-27T10:09:31Z")\
**Posts on this page:** 1\
**Showing post:** 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 27, 2023, 9:10pm UTC](https://discourse.julialang.org/t/rfc-some-ideas-to-tackle-15276-performance-of-captured-variables-in-closures/95260/5 "2023-02-27T21:10:38Z")

</div>

> [@mrufsvold](#):
>
> I don’t know how I feel about removing options or requiring the use of `Ref`.

To clarify, if we defend the behavior as it is now, this can never be type-stable:

```julia
julia> genf(a) = () -> (a += 1)
       f = genf(0)
       f(), f(), f()
(1, 2, 3)

```

to achieve type-stability, we must manually type-stabilize `a` with a `Ref`:

```julia
julia> geng(a) = let a=Ref(a); () -> (a[] += 1) end
       g = geng(0)
       g(), g(), g()
(1, 2, 3)

```

(~~as shown [here](https://discourse.julialang.org/t/writing-a-static-function/92882/20), simple type-annotation is an inferior solution.~~ Edit: per this proposal, type-annotation will be equivalent)

If we defend the current behavior, what we gain for it is the ability to accommodate a function’s return type changing during the life of a closure whose capture type depends on it:

```julia
julia> q() = 0
       genh(a) = () -> (a += q() + 1)
       h = gen(0)
       h(), h(), h()
(1, 2, 3)

julia> q() = 0.0
       h(), h(), h()
(4.0, 5.0, 6.0)

```

The only reason that this latter code functions, is because the box `h.a` is type-unstable. If we stabilize it based on knowledge of `q`’s return type at the time `h` is instantiated, then if `q`’s return type changes, `h` will throw an error.

In such a future where `a` is stabilized, if you wish for `h` to accommodate arbitrary changes in `q` to include changes of its return type, you would have to write this:

```julia
genx(a) = let a=Ref{Any}(a); () -> (a[] += q() + 1) end

```

In other words, currently type-instability is the default, and you have to add a `Ref` for type-stability. I suggest making type-stability the default instead—considering how much more useful type-stability is than the ability to accommodate arbitrary changes in functions’ return types, this seems like a good trade.

> [@](#):
>
> It would be extremely helpful if the tooling unloaded some of that burden.

Agree. All roads lead back to a “Julia Standard Linter™.” Even still, sometimes you really have to rip up your code to get type stability.

> [@Zach\_Christensen](#):
>
> A lot of that stuff currently seems to be implemented in the easiest way to make user facing stuff work

Yes, this has become apparent without looking at the code 😅

> Boxing may be a bit more complicated if we want a consistent approach to memory management in Julia. … asserting new rules for one part of the system in the absence of standardizing other parts may be premature

Can you elaborate your concerns? Is simply type-parameterizing `Core.Box` harmful somehow?

---

_[View the full topic](https://discourse.julialang.org/t/rfc-some-ideas-to-tackle-15276-performance-of-captured-variables-in-closures/95260)._
