If that’s really how it was implemented, _.x would simply be an Int, whose value would be set to whatever x was at construction time.

You can return an anonymous function that captures local variables without wondering what happens when the local variable goes out of scope. And that’s because it’s copied into closure, not passed as a reference to a local stack-allocated variable.

This doesn’t match up with the behavior in the rest of Julia for immutable types like Int: a variable name is supposed to just be a name that refers to a value. Assigning x=0; y=x doesn’t bind y to x; instead they both point at x's current value, and changing x doesn’t change y.
Even an actual reference (Ref) to an integer variable just becomes a reference to a copy of the variable!

julia> begin
x = 0
xref = Ref(x)
x = 1
xref[]
end
0

Lambdas are pass-by-copy by default in C++, which might be part of why I expected that here.

That said, I now understand that this is following the rules referenced here in the Performance Tips:

This style of code presents performance challenges for the language. The parser, when translating it into lower-level instructions, substantially reorganizes the above code by extracting the inner function to a separate code block. “Captured” variables such as r that are shared by inner functions and their enclosing scope are also extracted into a heap-allocated “box” accessible to both inner and outer functions because the language specifies that r in the inner scope must be identical to r in the outer scope even after the outer scope (or another inner function) modifies r .

The problem I have here, is that I can’t figure out the right way to get the behavior I wanted through closure. Inside the function, I can’t mark it local or anything, and it’s too late to make a copy.

Okay, so all that said, I’m sure there are interesting reasons that I don’t yet understand for why this is done. Are there situations where the boxing is necessary? I’d love to hear those if anyone can spare the time! Thanks!

The problem I have here, is that I can’t figure out the right way to get the behavior I wanted through closure. Inside the function, I can’t mark it local or anything, and it’s too late to make a copy.

A while ago there was some discussion that “freezing the captures” might make sense in FastClosureshttps://github.com/c42f/FastClosures.jl/issues/5. I didn’t get around to implementing that but most of the machinery is there. It also probably makes more sense than what I currently have in that package

NHDaly:

Okay, so all that said, I’m sure there are interesting reasons that I don’t yet understand for why this is done. Are there situations where the boxing is necessary? I’d love to hear those if anyone can spare the time! Thanks!

I’ve wondered about the exact reason for this as well. It seems like it’s been there from the very first closure lowering in https://github.com/JuliaLang/julia/commit/6ed5f4e89a995a4f0ff968a76f4906a865d4e334. It certainly makes closures with mutable state easy to construct but that seems a little at odds with modern julia preferring immutable structs, etc. It would be quite easy to explicitly use a Ref for occasions which require mutable state. @jeff.bezanson are there points of interest that we’re missing here?

I tend to agree with you. Coming from C++ I find the scheme-alike lexical scoping rules quite counter intuitive. Perhaps because of a dissonance between imperative vs functional styles. However, any alternative would seem to need a story about how mutually recursive inner functions would refer to each other.

We can at least have a macro which introduces independent bindings. FastClosures doesn’t actually do this (mainly because I didn’t fully understand the rules when I wrote it). But that could be fixed; see https://github.com/c42f/FastClosures.jl/issues/11.