Variable scope and capturing variables in blocks
A Gosu block maintains some context with respect to the enclosing statement that created the block. If code in the block refers to variables that are defined outside the block’s definition but are in scope where the block is defined, the variable is captured. The variable is incorporated by reference into the block. Incorporating the variable by reference means that blocks do not capture the current value of the variable at the time its enclosing code creates the block. If the variable changes after the enclosing code creates the block, the block uses the most recent value in the original scope. This behavior continues even if the original scope exited or finished.
The following example adds 10 to a value.
However, the value 10 was captured in a local variable, rather than included
in an argument. The captured variable, called captured in this example, is used
but not defined within the block:
var captured = 10
var addTo10 = \ x : int -> captured + x
var myresult = addTo10(10)
After the third line is executed, myresult contains the value 20.
A block captures the state of the stack
at the point of its declaration, including all variables and the special
symbol this, which represents
the current object. For example, this
can represent the current instance of a Gosu class running a method.
This capturing feature allows the block to access variables in scope at its definition. Capturing occurs even in the following cases:
- After passing the block as an argument to another function
- After the block returns to the function that defines it
- If code assigns the block to a variable and retains the variable indefinitely
- After the original scope exits or finishes
Each time the block runs, the block can access all variables declared in the original scope in which the block was defined. The block can get or set those variables. The values of captured variables are evaluated each time the block is executed, and can be read or set as needed. Captured variable values are not a static snapshot of the values at the time that the block was created.
The following example creates a block
that captures a variable (x)
from the surrounding scope. Next, the code that created the block changes
the value of x. Code calls
the block only after that change happens:
// Define a local variable, which is captured by a block
var x = 10
// Create the block
var captureX = \ y : int -> x + y
// Note: the variable "x" is now SHARED by the block and the surrounding context
// Now change the value of "x"
x = 20
// At the time the block runs, it uses the current value of "x".
// This value is NOT a snapshot of the value at the time the block was created
var z = captureX( 10 )
print(z) // Prints 30 --- Not 20!!!
The captured variable is shared by the original scope and the block that was created within that scope. The block references the variable itself, not merely its original value.
