As I continue to learn JavaScript, I continue to run in to closures. And while I had some understanding of what that word means, when I came across this blog post from Alan Sorkin, it helped clear a few things up. This post focuses on closures in Ruby, hopefully in a way that illustrates the concept of closures more generally, irrespective of language.
Closures defined
Sorkin defines closures as follows:
A closure is basically a function/method that has the following two properties:
- You can pass it around like an object (to be called later)
- It remembers the values of all the variables that were in scope when the function was created. It is then able to access those variables when it is called even though they may no longer be in scope.
This is much clearer than the other definitions we sometimes hear, which Sorkin alludes to as (1) “In computer science, a closure is a first-class function with free variables that are bound in the lexical environment” or (2) “A closure is a function that is said to be ‘closed over’ its free variables”. As we’ll see, these definitions have value, but what exactly do they mean? Let’s look at some code.
Code example in Ruby
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
|
When executed, this code prints: Value1: 5, Value2: some value
. So what’s going on here? As Sorkin describes, “the value_printer
function creates a closure, using the lambda construct, and then returns it. We then assign our closure to a variable and pass that variable to another function, which then calls our closure. This satisfies the first property of a closure – we can pass it around.”
Furthermore, “when we called our closure, we printed out 5
and some value
. Even though both the @value1
and value2
variables were both well and truly out of scope in the rest of the program when we finally called the closure; inside the closure they were still in scope as it retained the state of all the variables that were in scope when it was defined. And so, our lambda satisfies the second property also which makes it a closure.”
To the earlier point, we can now better understand the technical definitions. First-class function: a function that can be passed around like an object. Lexical environment: variables that are defined in the closure’s scope. These are what make a closure a closure.
Why?
This made closures a lot clearer to me, but a question remained: why? What are some use cases for this pattern? Sorkin argues that closures are especially useful in functional languages (which are inherently stateless) because “we can use closures to essentially store some state which will persist as long as our closure lives on (i.e. if the closure changes the value of a variable it will retain the new value the next time the closure is invoked).” This enables us to do more with less code.
In non-functional languages? Also to do more with less code. Closures in Ruby come in the form of blocks, lambdas (as in the code example above), and blocks, all of which provide advantages in refactoring, customization, iterating across collections, managing resources, and enforcing policy. There are a number of use cases, some of which are described in this overview.