Environment capture is a feature of computer programming languages when a variable value from an outer enclosing scope is referred to in an inner scope, and that inner scope is longer-lived than the outer scope. Thus the variable persists in the inner scope after the outer scope is defunct.

What?

Environment capture is originally from functional languages, which pioneered the idea that code blocks can be return values of functions. The returned code block is the inner scope which references variables in the enclosing code block. These variables in its environment are captured and kept after the enclosing scope is no longer being executed. The function plus environment is called a closure, and the operation of creating the closure is currying.

However the same or very similar concepts have in recent years cropped up languages whose heritage is mostly procedural and object-oriented. The two most familiar will be Java (version 1.1 or later), where inner classes can reference variables from the enclosing class and in C# (version 2.0 or later), where an anonymous method can reference variables from the enclosing scope.

How about an example?

In the Scheme dialect of LISP, enter these definitions

(define MultiplyBy
   (lambda (x)
      (lambda (y) (* x y))))

(define Triple (MultiplyBy 3))

And evaluate:
(Triple 4)

The result is of course
12

The function Triple is the MultiplyBy function with the value three bound into it. The value 3 in variable x has been captured. You can have any number of such definitions in scope at once, e.g.

(define Hundredfold (MultiplyBy 100))
(define PiTimes (MultiplyBy 3.14159265))

For a similar but slightly more complex example in ML see here: http://caml.inria.fr/pub/docs/manual-ocaml/manual003.html#htoc5

So how about something more mundane then?

In C# 1.0, fairly standard code is like this.

Button1.Click += new EventHandler(SayHello);

private void SayHello(object sender, System.EventArgs e)
{
   MessageBox.Show("Hello");
}

C# 2.0 allows you to abbreviate this with shorthand called an anonymous method: you don't need to specify the method name or parameters. Just give a block of code:

Button1.Click += delegate { MessageBox.Show("Hello"); };

However what happens if you do this?

private void SaySomething(object Something)
{
   Button1.Click += delegate 
     { MessageBox.Show(Something.ToString()) };
}

What is the lifespan of the variable Something? Normally it would fall out of scope and be eligible for garbage collection at the end of SaySomething. But you can see it still alive every time you press button1 – at least until SaySomething is called with a different object as parameter, and the click handler is replaced. This anonymous block of code has captured the value of Something from its original environment, so Something is in scope for at least as long as this code is bound to the button click handler.

This also shows how building this block of code at compile-time is not always the trivial expansion that it might appear from comparing the first two c# examples. In fact doing the same thing in c# 1.0 is not nearly as terse - you'd store Something in a private variable so that both methods can access it - essentially making it visible to all methods on this object in order to transfer it between two of them.

NB: C# anonymous methods do not copy values from the outer scope, they share them. "Anonymous methods are not closures or completions. They are anonymous methods. They allow sharing of variables with the outer method."

In Java

public class Stack {
    private Object items;

    //code for Stack's methods and constructors not shown

    public Iterator iterator() {
        return new StackIterator();
    }
    class StackIterator implements Iterator {
        int currentItem = items.size() - 1;

        public boolean hasNext() {
            ...
        }
        public Object next() {
            ...
        }
        public void remove() {
            ...
        }
    }
}

Here is a standard example of a Java class, a stack with an inner class that implements the Iterator interface. This is done so that the stack can have any number of iterators open at once, each with their own current item. But the iterator's data aside from the current item index is entirely from the outer containing class.

It might be that an iterator is the only reference left to this data, so the iterator is keeping the stack's variables alive much like in the C# example.

In JavaScript

JavaScript may look a bit like C, but it's an interpreted and loosely typed language, so you can do things in a manner not very different from Lisp:

multiplyBy = function(x) {
  return function(y) {
    return x*y;
  }
}

triple = multiplyBy(3);

triple(4); //output is 12

A more common task in JavaScript is to attach event handlers to web page events (such as mouse clicks). You can hook in a new event and keep the old event handler as a value captured in the new handler's environment like this:

// Javascript does not have a cross-browser way of adding 
// event listeners. You can work around this in numerous 
// ways. Here is one.

var old_event = window.onload; // get what is already scheduled to happen when window loads.

if (typeof window.onload != 'function') {
  window.onload = new_function; // just tell it to call your new function if nothing scheduled to happen.
} else { // set onload to a function which...
  window.onload = function() { 
    old_event();              // calls the old function
    new_function();           // calls the new function
  };
};

But do you want to do this?

Environment capture is not a bad thing, all round. In functional languages you come across it all the time, and it's a large part of the terse power of these languages. In Java and C# it is a nifty feature that can be used to produce more concise code. However if you don't know that it's there you might not always read other people's code correctly; and you might even do it by accident in your own code and cause unexpected behavior.


Thanks to themanwho for the Javascript code.

Log in or registerto write something here or to contact authors.