Understanding the closures debate

Does Java need closures? Three proposals compared

1 2 3 4 5 Page 5
Page 5 of 5

Control abstractions with closures

We've mentioned that the BGGA proposal includes lexical binding because the feature is needed for a fine-grained factoring out of common functionality. In fact, closures in BGGA are supposed to enable user-defined control structures, which ideally should be as easy to use as language features. The code to be abstracted in a refactoring might contain a break, continue, or return statement, and such control statements are not supposed to prevent refactoring. For this purpose, BGGA needs lexical binding of control statements.

In fact, non-local control statements enable interesting and powerful closures. Here is a prototypical example of a refactoring that BGGA aims to enable: the use of explicit locks such as java.util.concurrent.Lock. Consider a thread-safe stack abstraction that holds integers. It needs synchronization for its push and pop method and uses explicit locks for it:

Listing 12. Explicit locks in Java

public class Stack {
      private static final int SIZE = 256;
      private int[] arr = new int[SIZE];
      private int cnt = 0;
      private Lock lock = new ReentrantLock();
      public void push(int elm) {
        lock.lock();
        try {
          arr[cnt++] = elm;
        } finally {
          lock.unlock();
        }
      }
      public int pop() {
        lock.lock();
        try {
          return arr[--cnt];
       } finally {
          lock.unlock();
        }
      }
    }

Obviously, the control structure that acquires and releases the lock is repeated in both the push and the pop method and we might want to factor it out. For this purpose we define a control abstraction withLock, which is a method that takes a closure that represents the critical region.

Listing 13. Definition of control abstraction withLock using BGGA

public class Utils {
      public static <T> T withLock(Lock lock, { => T} block) {
        lock.lock();
        try {
          return block.invoke();
        } finally {
          lock.unlock();
        }
      }
    }

Using the withLock method we can refactor the push and the pop method as follows:

Listing 14. Refactoring push and pop using withLock

public void push(int elm) {
      Utils.withLock(lock, { =>
        arr[cnt++] = elm;
      });
    }
    public int pop() {
      Utils.withLock(lock, { =>
        return arr[--cnt];
     });
    }

Regarding readability the BGGA proposal goes even one step further and defines a special control invocation syntax that renders it even more readable. To make it perfect we add a static import statement for the withLock method and -- voilá-- the withLock control abstraction looks as pretty as the Java-language-defined synchronized keyword:

Listing 15. Control invocation syntax in BGGA

import static Utils.withLock;

    public void pushY(int elm) {
      withLock(lock) {
        arr[cnt++] = elm;
      }
    }
    public int popY() {
      withLock(lock) {
        return arr[--cnt];
      }
    }

Putting syntactic sugar aside (and getting back to the point of lexical binding and non-local return statements) it's evident that we truly need the lexical binding of the return statement here. The closure's return statement is supposed to return from the pop method, not just from the closure.

Alternate proposals for control abstraction

The withLock control abstraction is a striking example of where the BGGA proposal is headed: it is a key goal of BGGA to allow for reusable abstractions such as withLock that can be used to refactor existing programs and simplify future Java code. Neither CICE nor FCM have such an ambitious goal; instead both strive to simplify specific pieces of the Java syntax.

This said, both CICE and FCM have come up with proposals that address control structures. The Java Control Abstractions proposal suggests an addition to FCM that enables control structure in a way that is similar to what BGGA proposes. The main difference is that JCA deliberately distinguishes between regular use of closures and control constructs that use closures. Writing a control abstraction method such as withLock is more complex than writing a regular method with closures such as forEach. This is because the control abstraction method must take into account how return, continue, and break and exceptions are handled.

Interestingly, BGGA is heading in a similar direction. The latest ideas regarding restricted vs. unrestricted closures also introduce a distinction between closure in control abstraction methods and regular use of closures. Eventually, we might see the two proposals converge.

Meanwhile, Automatic Resource Management Blocks is an addition to CICE that takes an entirely different approach to control abstractions. The observation is that much of the need for control abstractions circles around try-finally constructs and that there is basically a need for automatic resource release. For this reason, the proposal suggests language support for automatic resource disposal at the end of a block and an interface that would be used to make a class eligible for this kind of automatic disposal.

In conclusion

From a technical viewpoint the three closure proposals along with their respective companion proposals cover similar ground and strive for two main goals:

  1. Simplify the syntax for implementing and using certain simple interfaces (with just one method), such as Runnable, ActionListener, and Comparator.
  2. Allow common control structures to be refactored into shared utilities by means of closures; that is, enable user-defined control structures that are as easy to use as language features.

All three proposals clearly address the first goal, each in a slightly different way. Strictly speaking, the second goal is met only by BGGA and FCM+JCA. The CICE proposal takes an entirely different approach that does not aim to support custom-control structures, but suggests embedding into the language better support for resource management. Here the goals simply diverge.

In summary: CICE+ARM suggests a simple, easy-to-use feature with minimal modifications of the language; FCM+JCA is more ambitious; and BGGA proposes to add considerably more expressive power and flexibility to the Java language. The flipside of BGGA is quite a bit of complexity and fairly extensive changes to the language.

The closures debate in essence

Given the intensity with which this topic is occasionally discussed, you might get the feeling that closures are a matter of life or death for Java or your Java career. This argument is used by both people who want closures in Java and those who oppose them -- or who might be willing to settle for the comparably simplistic CICE proposal.

An interesting Java.net opinion poll has been collecting "votes" for the three closures proposals for some time. In the poll, CICE and "no closures" are listed as two separate options, although their supporters want essentially the same thing: to keep the Java language simple. Combined, the "no closures" and CICE options have roughly 42% of the vote, which is roughly equal to the share of BGGA. FCM has nearly 15% of the votes, and less than 1% of voters have voted for "something else."

Those who want closures in Java argue that if Java rejects closures, programmers will turn to other languages that support them well. (Even worse: it could be a language from the Microsoft .NET family.) Those who object to closures in Java argue that their inclusion would complicate the language beyond normal usability: mainstream programmers will turn away from Java, so the argument goes, and move on to simpler programming languages. In essence, those against closures are afraid that Java will turn into a guru language used only by experts in niche areas.

The "too complicated" argument is often heard in combination with a reference to generics, which many people also believe are too complex for what they achieve. The sentiment is that the evolution of Java toward ever increasing complexity must stop. As Joshua Bloch put it, we have "used up our complexity budget on generics, and in particular, on wildcards."

Basically, there are two major opposing camps: one in favor of simplicity of use and minimal modification of the language, and the other in favor of increased expressive power at the expense of additional complexity. Which position is preferable is in the eye of the beholder. If we may risk a gaze into our crystal ball we would predict that one side-effect of the heated discussion is that we will not see closures in Java 7. (At the time of this writing, there is not yet a votable JSR for Java SE 7.)

While it may seem to some that the closures debate has already drawn on for long enough, the intensity of the debate, and the seemingly even support for BGGA and CICE/no closures (according to one poll) suggest that the delay is not unreasonable. At the least it will give us all plenty of time for further discussions.

Klaus Kreft has 20+ year of experience in the software business and currently works as a software engineer and system architect at SEN (Siemens Enterprise Communications GmbH & Co. KG).

Angelika Langer works worldwide as a freelance instructor, coach, and author with an independent curriculum of C++ and Java courses. Her areas of expertise include advanced C++ and Java programming, concurrent programming, and performance issues. She is the author of the Java Generics FAQ, an online resource covering new language features in Java 5.0, a Java champion, and a regular speaker at Java conferences.

Together, Klaus and Angelika author the column "Effective Java" for the German magazine JavaMagazin.

Learn more about this topic

Closures proposals and prototypes

More

1 2 3 4 5 Page 5
Page 5 of 5