Lambda

language feature

tags:

As there is a growing interest in dynamic languages, more people are running into a programming concept called Lambdas (also called Closures, Anonymous Functions or Blocks). People from a C/C++/Java/C# language background don't have lambdas 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.

Lambdas 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 lambda 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 Lambdas, 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 lambda, 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". These mechanisms are similar to lambdas, but there are two telling differences.

The first one is a formal difference, lambdas usually define closures - which means they 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 lambdas in languages that don't have real closures can't do that. Lambdas allow you to do even more interesting stuff. Consider this function.

def paidMore(amount)
  return lambda {|e| e.salary > amount}
end

This function returns a lambda, indeed it returns a lambda 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 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 lambdas they usually create closures - a block of code plus the bindings to the environment they came from. You can have lambdas that don't create closures, but such animals aren't very useful and are thus not very common - which is why closure is often used as an alternative term for lambda. [1][2]

The second difference is less of a defined formal difference, but is just as important, if not more so in practice. Languages that support lambdas 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 lambdas 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.

I use lambdas a lot in Ruby, but I don't tend to create them explicitly and pass them around. Most of the time my use of lambdas is based around CollectionLambdas 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 lambdas 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.

Notes

1: When I first published this bliki entry in 2004 I used the term "Closure" to refer to these languages features. At the time "closure" was often used in this way. Since then the term "lambda" became more popular, so I changed this bliki entry to follow the usage.

2: Java's anonymous inner classes can access locals - but only if they are final.