Skip to content
Closures — Functions That Capture State
step 1/3

Reading — step 1 of 3

Closures — Capturing the Environment

~2 min readClosures & Classes

Closures — Functions That Capture State

A closure is a function that "closes over" variables from its enclosing scope, retaining access to them even after the enclosing function has returned. Closures are one of the most powerful features in programming languages.

The Key Insight

fun counter() {
    var n = 0;
    fun inc() {
        n = n + 1;
        return n;
    }
    return inc;
}

var c = counter();
print c();   // 1
print c();   // 2
print c();   // 3

When counter() returns, its local variable n would normally be destroyed. But inc still references n — so n must be kept alive. The function inc together with the captured variable n forms a closure.

How Closures Work

When we create a function, we store a reference to the current environment — the environment at the point of creation. This is the function's closure environment:

Function {
    name:    "inc"
    params:  []
    body:    [...]
    closure: <environment where n lives>
}

When inc is later called (even after counter has returned), it executes in a new environment enclosed by the closure environment — not the caller's environment. This is why n is still accessible.

Multiple Closures Sharing State

Two closures can capture the same variable:

fun pair() {
    var x = 0;
    fun get() { return x; }
    fun set(val) { x = val; }
    return get;  // (imagine returning both somehow)
}

Both get and set close over the same x. Changes through set are visible to get. This is essentially how objects work — closures are the "poor man's objects" (and objects are the "poor man's closures").

How JavaScript Handles Closures

JavaScript closures famously cause bugs in loops:

javascript

All three functions close over the same i, which is 3 after the loop. This is why let was introduced — it creates a new variable per iteration. Our language avoids this because for desugars with a block scope.

Lua's Upvalues

Lua pioneered the concept of upvalues — a flat list of captured variables stored with each closure. In Crafting Interpreters, the bytecode VM (clox) uses this same approach. Each upvalue is either a direct reference to a stack slot or a reference to an already-captured upvalue from an enclosing function.

Your Task

Ensure your interpreter correctly handles closures. Functions must capture their enclosing environment and retain access to it even after the enclosing function returns.

Discussion

Ask a question, share an insight, or help someone who’s stuck.

Sign in to post a comment or reply.

Loading…