This is an earlier edition of the documentation. To build counters with separate or shared state, open the current guide Closures and Counter State.
Closures are one of the most powerful features in Lispex. They are the mechanism that allows a function to "remember" the environment in which it was created. This guide will walk you through what a closure is and why it's so useful.
What is a Closure?
In simple terms, a closure is a function bundled together with its "backpack". This backpack contains all the variables that were in scope at the moment the function was created.
This means a function can access those "remembered" variables, even if it's called from a completely different part of your program.
Let's explore this with the most common pattern: a "function factory."
The Function Factory Pattern
Imagine you need many functions that do similar things. For example, one function that adds 5 to a number, another that adds 10, and another that adds 100. Instead of writing each one by hand, you can create a "factory" that manufactures these functions for you.
Step 1: Write the Factory Function
Our factory, make-adder, will take one argument, amount-to-add, and its job is to return a new function.
(define (make-adder amount-to-add)
(lambda (n)
(+ n amount-to-add)))Step 2: The "Magic" of the Closure
When we call (make-adder 5), here’s what happens:
- A new, temporary environment is created for the
make-addercall. - Inside this environment, the parameter
amount-to-addis bound to the value5. - The
lambdafunction is created. As it is created, it looks around and packs all the variables it can see into its "backpack". It seesamount-to-addis5, so it packs that information away. make-adderfinishes and returns thelambdafunction, complete with its backpack containingamount-to-add = 5.
This returned function--the lambda plus its backpack--is a closure.
Step 3: Use the Created Functions
Now we can create specialized functions using our factory:
;; Create a function that will always add 5.
(define add-five (make-adder 5))
;; Create a function that will always add 10.
(define add-ten (make-adder 10))
;; Let's use them!
(add-five 100)
;; Result: 105
;; The `add-five` function reaches into its "backpack" and remembers that
;; `amount-to-add` was 5.
(add-ten 100)
;; Result: 110
;; The `add-ten` function has its own, separate backpack where
;; `amount-to-add` was 10.Why is This Useful?
This pattern is incredibly powerful. It allows you to create customized, pre-configured functions on the fly. You can use it for:
- Creating configurable event handlers.
- Setting up functions with initial state.
- Advanced patterns like currying and partial application.
Conclusion
Understanding closures is key to mastering functional programming in Lispex. Just remember: it's a function + its creation environment (backpack). With this concept, you can write more elegant, modular, and reusable code.
For a more formal definition, you can revisit the current Procedures and Closures manual page.