It's probably rare that this would be used with a global variable in a real script. Limitations of the current implementation would prevent this from being used safely with local variables, since each local variable is in fact a sort of singleton object; the variable always belongs to the
top-most instance of the function still running. For a bound function being called, that would often not be the instance of the function that bound the variable, as that instance may have returned.
Closures work around this by using the concept of "free variables". Any local variable which may be captured by a closure is actually an alias for an unnamed "free variable". Each instance of the function allocates a block of free variables which are used for this purpose. When the closure is instantiated, it captures a reference to this block. These pointers are then shoved into the closure's own local aliases when the closure is called, so that it refers to the appropriate memory space.
Code: Select all
outer() {
a := 1
inner() {
return a
}
return Func("inner")
}
outer and
inner each have their own local variable named
a, which is (in
both cases) an alias for a "free variable". That is, an alias in the same sense as ByRef: the name
a is associated with a
Var struct, and that struct contains a pointer to some other
Var where the real value can be found.
- Within outer, a is an alias of a free variable belonging to the instance of outer which is currently executing.
- Within inner, a is an alias of a free variable belonging to the instance of outer which is associated with whichever closure instance was called.
These variables cannot be
individually bound or captured, because the free variables are allocated in a block and only the overall block is reference counted. Reference counting is not performed when each individual alias is assigned or cleared; only when a closure is instantiated or freed, or its outer function is called/returns.
If a local variable is not referenced by any closures (even a closure which is never used), it is not an alias--it always corresponds to the function's top-most running instance, and will be freed when the function returns. Binding it would not give correct results if that instance of the function is either not running or not the top-most instance when the bound function is called.
(The values of local variables are "backed up" when a new instance of the function interrupts a previous instance, and "restored" when it returns. This approach is optimal for functions which only ever have at most one instance, as the local variables do not need to be fully allocated and freed each time the function is called, and their addresses can be resolved at load time.)