|
As there is a growing interest in dynamic languages, more people
are running into a programming concept called Closures or
Blocks. People from a C/C++/Java/C# language background don't have closures
and as a result aren't sure what they are. Here's a brief
explanation, those who have done a decent amount of programming in
languages that have them won't find this interesting. Closures have been around for a long time. I ran into them
properly for the first time in Smalltalk where they're called
Blocks. Lisp uses them heavily. They're also present in the Ruby
scripting language - and are a major reason why many rubyists like
using Ruby for scripting. Essentially a closure is a block of code that can be passed as an
argument to a function call. I'll illustrate this with a simple
example. Imagine I have a list of employee objects and I want a list
of those employees who are managers, which I determine with an
IsManager property. Using C#, I'd probably write it like this.
public static IList Managers(IList emps) {
IList result = new ArrayList();
foreach(Employee e in emps)
if (e.IsManager) result.Add(e);
return result;
}
In a language that has Closures, in this case Ruby, I'd write
this.
def managers(emps)
return emps.select {|e| e.isManager}
end
Essentially select is a method defined on the Ruby collection
class. It takes a block of code, a closure, as an argument. In ruby
you write that block of code between curlies (not the only way). If the block of code
takes any arguments you declare those between the vertical
bars. What select does is iterate through the input array, executes
the block of code with each element, and returns an array of those
elements for which the block evaluated as true. Now if you're a C programmer you probably think "I could do
that with a function pointer", if you're a Java programmer you
probably think "I could do that with an anonymous inner class", a
C#er would consider a delegate. These mechanisms are similar to
closures, but there are two telling differences. The first one is a formal difference, closures can refer to
variables visible at the time they were defined. Consider this
method
def highPaid(emps)
threshold = 150
return emps.select {|e| e.salary > threshold}
end
Notice that the code in the select block is referring to a local
variable defined in the enclosing method. Many of the alternatives
to closures in languages that don't have real closures can't do
that. Closures allow you to do even more interesting stuff. Consider
this function.
def paidMore(amount)
return Proc.new {|e| e.salary > amount}
end
This function returns a closure, indeed it returns a closure whose
behavior depends on the argument sent into it. I can create such a
function with and assign it to a variable.
highPaid = paidMore(150)
The variable highPaid contains a block of code
(called a Proc in Ruby) that will return whether a tested object
has a salary greater than 150. I might use it like this.
john = Employee.new
john.salary = 200
print highPaid.call(john)
The expression highPaid.call(john) calls the
e.salary > amount code I defined earlier, with the
amount variable in that code bound the to the 150 I
passed in when I created the proc object. Even if that 150
value went out of scope when I issue the print call, the binding
still remains. So the first crucial point about closures is that they are a
block of code plus the bindings to the environment they came
from. This is the formal thing that sets closures apart from
function pointers and similar techniques.(Java's anonymous inner
classes can access locals - but only if they are final.) The second difference is less of a defined formal difference, but
is just as important, if not more so in practice. Languages that
support closures allow you to define them with very little syntax.
While this might not seem an important point, I believe it's
crucial - it's the key to make it natural to use them frequently.
Look at Lisp, Smalltalk, or Ruby code and you'll see closures all
over the place - much more frequently used than the similar
structures in other languages. The ability to bind to local
variables is part of that, but I think the biggest reason is that
the notation to use them is simple and clear. A good case in point is what happened when ex-Smalltalkers started
in Java. Initially many people, including me, experimented with
using anonymous inner classes to do many of things that we'd done
with blocks in smalltalk. But the resulting code was just too messy
and ugly so we gave up. Like any term in software there is a lot of blur about the exact
definition of closure. Some people say that the term only applies to
an actual value that includes bindings from its environment, such as
the value returned by highPaid. Others use the term
'closure' to refer to a programming construct that has the ability to
bind to its environment. This debate, an example of the
TypeInstanceHomonym, is usually carried out with the
politeness and lack of pedantry that our profession is known for. I use closures a lot in Ruby, but I don't tend to create Procs
and pass them around. Most of the time my use of closures is based
around CollectionClosureMethods similar to the select
method I showed earlier. Another common use is the 'execute around
method', such as when processing a file.
File.open(filename) {|f| doSomethingWithFile(f)}
Here the open method opens the file, executes the supplied block,
and then closes it. This can be a very handy idiom for things like
transactions (remember to commit or rollback) or indeed anything
where you have to remember to do something at the end. I use this
extensively in my xml transformation routines. Such use of closures is actually much less than what people in
the Lisp and functional programming worlds do. But even with my
limited uses I miss them a lot when programming in languages without
them. They are one of those things that seem minor when you first
come across them, but you quickly grow to like them. Other languages: C#
3.0 |
C# 2.0 |
Python |
Boo |
Lisp |
JavaScript Neal Gafter has an excellent posting on the history of
closures. Vadim Nasardinov led me to this interesting
tidbit of history of closures in Java from Guy Steele.
|