Chapter 37. Closure

A block of code that can be represented as an object (or first-class data structure) and placed seamlessly into the flow of code by allowing it to reference its lexical scope.

var threshold = ComputeThreshold();
var heavyTravellers = employeeList.FindAll(e => e.MilesOfCommute > threshold);

Also known as: lambda, block, or anonymous function

You have a collection of objects and want to filter them in various ways. Writing a method for each filter leads to duplication in the setup and processing of the filter.

By using a Closure, you can factor the setup and processing of the filter and pass in an arbitrary block of code for each filter condition.

37.1 How It Works

Closures are a language feature that, despite being around for a long time, has only recently begun to make its way onto the radar of many software developers. This is probably because the languages that have and use closures, such as Lisp and Smalltalk, weren’t part of the C culture that drove the development of the current mainstream languages.

I use the term Closure in this book, but naturally there is no standard term for this language element. You also see them referred to as lambdas, anonymous functions, and blocks. Each language that uses them usually has its own term for them. For example, lispers use “lambda,” smalltalkers and rubyists use “block.” Although they are called blocks in Smalltalk and Ruby, it isn’t the same as blocks in C-based languages.

Now that I’ve got the terminological babble out of the way, I can actually say what they are. Here’s my starting definition: A Closure is a code fragment that can be treated as an object. To get serious about this, we need an example.

Let’s consider the problem of getting a subset of data from a collection. Imagine we have a list of employees and we want all employees who are heavy travelers.

image

Somewhere else in the code, we need to get a list of employees who are managers.

image

These two code fragments contain a lot of duplication. In both cases, we want a list that is formed by taking the members of the original list, running a Boolean function against each element, and collecting those for which the function returns true. Removing that duplication is a simple thing to envisage, but difficult to write in many languages because the thing that varies between the two code fragments is a chunk of behavior—which is often not easy to parametrize.

The most obvious way to parametrize something like this is to turn it into an object. What I need is a method on a list that will allow me to select from the list based on a separate object that I pass in.

image

I can then use it to select managers like this:

image

There’s a certain programming satisfaction in doing this, but there’s so much code in setting up the predicate object that the cure is worse than the disease. This is especially true when we look at the heavy travelers case. Here I need to pass a parameter into the predicate object, which means I need a constructor in my predicate:

image

Essentially, a Closure is a more elegant solution to this problem—one that makes it much easier to create a hunk of code and pass it around like an object.

You’ll notice I’ve made my examples in C#. I did this because in the past few years, C# has evolved steadily towards a more convenient use of Closures. C# 2.0 introduced the notion of anonymous delegates, which are a big step in this direction. Here’s the heavy traveler example again using anonymous delegates:

image

The first thing to notice here is that there’s much less code involved. The duplication between this expression and a similar one for finding managers is vastly reduced. In order to make this work, I’ve used a library function on the C# list class, similar to the select function I wrote myself for the handwritten predicate. C# 2 introduced a number of changes to the libraries that took advantage of delegates. This is an important point—for Closures to be really useful in a language, the libraries need to be written with Closures in mind.

A third point that this fragment illustrates is how easy it is to use the threshold parameter—I just use it in my Boolean expression. I can put any local variable that’s in scope into this expression, which saves all the faffing around with parameters that the predicate object version needed.

This reference to variables in scope is what formally makes this expression a Closure. The delegate is said to close over the lexical scope of where it’s defined.

Even if we take the delegate and store it somewhere for later execution, those variables are still visible and usable. Essentially, the system needs to take a copy of the stack frame to allow the Closure to still have access to everything it should see. Both the theory and implementation of this are quite tricky—but the result is very natural to use.

(Some people define “closure” only to mean an instantiated hunk of code that closes over some variables in lexical scope. As is often the case, the use of the word “closure” is very inconsistent.)

C# 3 went a step further. Here’s the heavy travelers expression again:

var threshold = ComputeThreshold();
var heavyTravellers = employeeList.FindAll(e => e.MilesOfCommute > threshold);

You’ll notice there’s really little change here—the main factor is that the syntax is much more compact. This may be a small difference but it’s a vital one. The usefulness of Closures is directly proportional to how terse they are to use. This syntax makes them far more readable.

There is a second difference, which is an important part of making the syntax terser. In the delegate example, I needed to specify the type of the parameter Employee e. I don’t need to indicate that type with the lambda because C# 3.0 has a type inference capability, meaning that, since it can figure out the type of the result of the right-hand side of the assignment, you don’t have to specify it on the left.

The consequence of all this is that I can create Closures and treat them just like any other object. I can store them in variables and execute them whenever I wish. To illustrate this, I can make a club class that has a field for a selector:

image

and use it like this:

image

This code creates a club in one function, using a parameter to set the threshold. The club contains the Closure, including the link to the now-out-of-scope parameter. I can then use the club to execute the Closure at any future time.

In this case, the selector Closure isn’t actually evaluated when it’s created. Instead, we create it, store it, and evaluate it later (possibly multiple times). This ability to create a block of code for later execution is what makes Closures so useful for Adaptive Models.

Another language featured in this book that uses Closures heavily is Ruby. Ruby was built with Closures from early days, so most Ruby programs and libraries use them extensively. Defining a club class looks like this in Ruby:

image

and we use it like this:

image

In Ruby, we can define a Closure either with curly braces, as above, or with a do...end pair.

image

The two syntaxes are almost entirely equivalent. In practice, people use the curlies for one-liners and the do...end for multiline blocks.

The sad part about this nice Ruby syntax is that you can only use it to pass a single Closure into a function. If you want to pass multiple Closures, you have to use a less elegant syntax.

37.2 When to Use It

Like many programmers who have used languages with good support for Closures, I find I miss them a great deal when using a language without them. They are a valuable tool to take chunks of logic and arrange them to eliminate duplication and support custom control structures.

Closures play a couple of useful roles in DSLs. Most obviously, they are an essential element for Nested Closure. They also can make it easier to define an Adaptive Model.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
3.15.29.119