Detailed Explanation of Variable Capture in Closures

Detailed Explanation of Variable Capture in Closures

  1. Is tricky. Will come onto it in a minute.
  2. There's no difference - in both cases, it's the variable itself which is captured.
  3. Nope, no boxing occurs.

It's probably easiest to demonstrate how the capturing works via an example...

Here's some code using a lambda expression which captures a single variable:

using System;

class Test
{
static void Main()
{
Action action = CreateShowAndIncrementAction();
action();
action();
}

static Action CreateShowAndIncrementAction()
{
Random rng = new Random();
int counter = rng.Next(10);
Console.WriteLine("Initial value for counter: {0}", counter);
return () =>
{
Console.WriteLine(counter);
counter++;
};
}
}

Now here's what the compiler's doing for you - except that it would use "unspeakable" names which couldn't really occur in C#.

using System;

class Test
{
static void Main()
{
Action action = CreateShowAndIncrementAction();
action();
action();
}

static Action CreateShowAndIncrementAction()
{
ActionHelper helper = new ActionHelper();
Random rng = new Random();
helper.counter = rng.Next(10);
Console.WriteLine("Initial value for counter: {0}", helper.counter);

// Converts method group to a delegate, whose target will be a
// reference to the instance of ActionHelper
return helper.DoAction;
}

class ActionHelper
{
// Just for simplicity, make it public. I don't know if the
// C# compiler really does.
public int counter;

public void DoAction()
{
Console.WriteLine(counter);
counter++;
}
}
}

If you capture variables declared in a loop, you'd end up with a new instance of ActionHelper for each iteration of the loop - so you'd effectively capture different "instances" of the variables.

It gets more complicated when you capture variables from different scopes... let me know if you really want that sort of level of detail, or you could just write some code, decompile it in Reflector and follow it through :)

Note how:

  • There's no boxing involved
  • There are no pointers involved, or any other unsafe code

EDIT: Here's an example of two delegates sharing a variable. One delegate shows the current value of counter, the other increments it:

using System;

class Program
{
static void Main(string[] args)
{
var tuple = CreateShowAndIncrementActions();
var show = tuple.Item1;
var increment = tuple.Item2;

show(); // Prints 0
show(); // Still prints 0
increment();
show(); // Now prints 1
}

static Tuple<Action, Action> CreateShowAndIncrementActions()
{
int counter = 0;
Action show = () => { Console.WriteLine(counter); };
Action increment = () => { counter++; };
return Tuple.Create(show, increment);
}
}

... and the expansion:

using System;

class Program
{
static void Main(string[] args)
{
var tuple = CreateShowAndIncrementActions();
var show = tuple.Item1;
var increment = tuple.Item2;

show(); // Prints 0
show(); // Still prints 0
increment();
show(); // Now prints 1
}

static Tuple<Action, Action> CreateShowAndIncrementActions()
{
ActionHelper helper = new ActionHelper();
helper.counter = 0;
Action show = helper.Show;
Action increment = helper.Increment;
return Tuple.Create(show, increment);
}

class ActionHelper
{
public int counter;

public void Show()
{
Console.WriteLine(counter);
}

public void Increment()
{
counter++;
}
}
}

Understanding variable capture by closures in Javascript/Node

I don't have a handy reference. But the bottom line is: In the first, you're explicitly passing in i to an anonymous function, which creates a new scope. You are not creating a new scope for either i or j in the second. Also, JavaScript always captures variables, not values. So you would be able to modify i too.

The JavaScript var keyword has function scope, not block scope. So a for loop does not create a scope.

As a note, the non-standard let keyword has local scope.

Understanding javascript closure variable capture in v8

The standard doesn't say anything about garbage collection, but gives some clues of what should happen.
Reference : Standard

An outer Lexical Environment may, of course, have its own outer
Lexical Environment. A Lexical Environment may serve as the outer
environment for multiple inner Lexical Environments. For example, if a
Function Declaration contains two nested Function Declarations then
the Lexical Environments of each of the nested functions will have as
their outer Lexical Environment the Lexical Environment of the current
execution of the surrounding function."

Section 13 Function definition
step 4: "Let closure be the result of creating a new Function object as specified in 13.2"

Section 13.2 "a Lexical Environment specified by Scope" (scope = closure)

Section 10.2 Lexical Environments:
"The outer reference of a (inner) Lexical Environment is a reference to the Lexical Environment that logically surrounds the inner Lexical Environment.

So, a function will have access to the environment of the parent.

How closure captures values in Swift?

From Swift Programming Guide - Closures

A closure can capture constants and variables from the surrounding context in which it’s defined. The closure can then refer to and modify the values of those constants and variables from within its body, even if the original scope that defined the constants and variables no longer exists.

A closure captures variables, not the contents of variables. When we are talking about local variables in a function (which are normally allocated on stack), it makes sure they are accessible even when the function exits and other local variables are deallocated, therefore we can do things like this:

func myFunc() {
var array: [Int] = []

DispatchQueue.main.async {
// executed when myFunc has already returned!
array.append(10)
}
}

Your example is similar. The closure captures the variable. This is a variable on module level, therefore its scope always exists. When you reassign its value, it will affect the value read inside the closure.

Or, in other words, the closure will be equivalent to:

var closure = {
print(CurrentModule.element?.name ?? "default value")
}

where CurrentModule is the name of your main module (which is usually the name of your project).

To prevent this behavior and capture the value of the variable instead, we can use closure capture list. Unfortunately, the official documentation does not properly explain what exactly is a capture list. Basically, using a capture list you declare variables local to the closure using values that are available when the closure is created.

For example:

var closure = { [capturedElement = element] in
print(capturedElement?.name ?? "default value")
}

This will create a new variable capturedElement inside the closure, with the current value of variable element.

Of course, usually we just write:

var closure = { [element] in
print(element?.name ?? "default value")
}

which is a shorthand for [element = element].

Variable capture by closures in Swift and inout parameters

David's answer is totally correct, but I thought I'd give an example how capture actually works as well:

func captureMe() -> (String) -> () {

// v~~~ This will get 'captured' by the closure that is returned:
var capturedString = "captured"

return {

// The closure that is returned will print the old value,
// assign a new value to 'capturedString', and then
// print the new value as well:

println("Old value: \(capturedString)")
capturedString = $0
println("New value: \(capturedString)")
}
}

let test1 = captureMe() // Output: Old value: captured
println(test1("altered")) // New value: altered

// But each new time that 'captureMe()' is called, a new instance
// of 'capturedString' is created with the same initial value:

let test2 = captureMe() // Output: Old value: captured
println(test2("altered again...")) // New value: altered again...

// Old value will always start out as "captured" for every
// new function that captureMe() returns.

The upshot of that is that you don't have to worry about the closure altering the captured value - yes, it can alter it, but only for that particular instance of the returned closure. All other instances of the returned closure will get their own, independent copy of the captured value that they, and only they, can alter.

Using a Swift closure to capture a variable

The basic idea is that getPlayerID should not return anything, but rather should just have a parameter which is a closure, and once you retrieve the value you want to "return", you call the closure using that value as a parameter.

But, I'd suggest all sorts of other refinements here:

  • Build an array of the strings and return that
  • Check to see if result is a .Failure because you have no control over what various server/network issues may arise
  • Change the closure to detect and report errors

But hopefully this illustrates the basic idea:

Personally, I'd (a) make the String parameter to the completionHandler optional; (b) add another optional error parameter; and (c) add error handling to the getPlayerID:

func getPlayerID(completionHandler: ([String]?, ErrorType?) -> Void) {
Alamofire.request(.GET, "URL", headers: headers)
.responseJSON { request, response, result in
switch (result) {
case .Success(let value):
let json = JSON.self(value)

// variable to hold all of the results

var strings = [String]()

// populate the array of strings

for var index = 0; index < json.count; index++ {
if json[index]["Name"].stringValue == named {
strings.append(json[index]["FOO"].stringValue)
}
}

// call the completion handler with the strings

completionHandler(strings, nil)
case .Failure(_, let error):
completionHandler(nil, error)
}
}
}

And then, when you want to call it:

getPlayerID() { strings, error in
// use `strings` here
}

// but not here


Related Topics



Leave a reply



Submit