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

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

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> geng(a) = let a=Ref(a); () -> (a[] += 1) end
       g = geng(0)
       g(), g(), g()
(1, 2, 3)

(as shown here, 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> 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:

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.

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.

Yes, this has become apparent without looking at the code :sweat_smile:

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?