Tuesday, 1 January 2008

Closures - Comparing control structures of BGGA, ARM and JCA

In this blog I'm going to compare the second major part of the three principle 'closure' proposals - control structures. This follows my previous blog where I compared one method callbacks.

Here are the links to the proposals if you want to read more:

  • BGGA - full closures for Java
  • CICE - simplified inner classes (with the related ARM proposal)
  • FCM - first class methods (with the related JCA proposal)

Comparing 'closure' 'control structure' proposals

Firstly, what do we mean by control structure problems? Well, there are three classic use cases:

 // resource management
 try {
   // open resource
   // use resource
 } finally {
   // close resource
 }

 // locking
 try {
   // lock
   // process within the lock
 } finally {
   // unlock
 }

 // looping around a map
 for (Map.Entry<String,Integer> entry : map.entrySet()) {
   String key = entry.getKey();
   Integer value = entry.getValue();
   // process map
 }

Each of the three principle closure proposals provide a solution to some or all of these issues.

BGGA

BGGA treats control structures as simply an alternative way to invoke a method. This allows any developer to write control structures, something referred to as library-defined control structures.

Thus, with BGGA, you can write a method to manage a lock. This can be called in two ways - either by passing the closure as a parameter, or by following the method call by the block.

 // library defined control structure
 public void withLock(Lock lock, {=>void} block) {
   lock.lock();
   try {
     block.invoke();
   } finally {
     lock.unlock();
   }
 }

 // call using closure as parameter
 withLock(lock, {=>
   // process within the lock
 });

 // call using control invocation syntax
 withLock(lock) {
   // process within the lock
 }

Whichever style is used, the meaning of return and this is the same. The return keyword will return from the lexically enclosing method, which is entirely sensible for control invocation. The this keyword also refers to the lexically enclosing class.

CICE/ARM

CICE does not support control structures. Instead, this area is covered by the ARM proposal.

The basic idea behind the ARM proposal is that control structures should only be added by language designers, and should not be able to be added to libraries.

The ARM proposal itself is merely an outline proposal and does not have detail. However, Josh Bloch has indicated that he would see ARM tackling specific issues, notably resource management and locking.

 // resource management
 try (BufferedReader r = new BufferedReader(new FileReader(file))) {
   // process resource
 }

 // locking
 protected(lock) {
   // process within the lock
 }

As this is a language change, it has to wait until a new version of Java is released. In addition, the meaning of return and this is naturally as per any other language defined control statement, such as a normal try block.

FCM/JCA

FCM does not support control structures. Instead, this area is covered by the JCA proposal.

The basic idea behind the JCA proposal is that control structures can be added by anyone, but that they should clearly be separated from the one method callback cases. The aim of the separation is to send a message to developers - adding a control structure in a library is something that should be undertaken with care.

 // library defined control structure
 public void withLock(#(void()) block : Lock lock) {
   lock.lock();
   try {
     block.invoke();
   } finally {
     lock.unlock();
   }
 }

 // call
 withLock(lock) {
   // process within the lock
 }

It should be noted that this is similar to the BGGA design. The calling code is identical to BGGA's control invocation syntax. However, you cannot pass the block of code as a parameter to the method as you can in BGGA - there is only one calling syntax.

The library defined control structure is very different however. In JCA, the library method has a special syntax with the block before the colon, and the input parameters after the colon. This matches the callers syntax, and clearly indicates that this is a 'special' kind of method.

Again, the meaning of return and this is lexically scoped, as with BGGA and ARM.

Comparison

Each of the three approaches has merits.

BGGA provides a full-featured approach, where a closure is treated as it would be in most other languages. Control structures can be written in libraries, and the control invocation syntax is just sugar for a normal method call.

ARM provides a use-case based approach. It solves specific problems with specific language changes. This doesn't allow ordinary developers to add control structures, but does provide valuable, reliable, enhancements.

JCA sits between BGGA and ARM, but because it allows library-defined control structures it is closer to BGGA. It differs from BGGA in that it treats control structures as effectively a separate language change to one method callbacks. By doing this, and with specific syntax, it aims to slightly discourage the extensive use of library-defined control structures.

Summary

There are two basic approaches to control structures - let language designers write them, eg. ARM, or let anyone write them, eg. BGGA or JCA. The BGGA and JCA proposals differ mainly on syntax, where JCA is using the syntax to warn against excessive or inappropriate use of control structures.

My own view is that everyone should have the right to write control structures, and that it will allow the Java language to keep fresh without lots of pressure on Sun to add new language features.

However, the right to add control structures comes with responsibilities. If everyone writes control structures all the time, then the resulting code might look more like a dialect of Java than Java itself. That is why I favour JCA, which aims through syntax and documentation to encourage serious consideration before writing a control structure.

Opinions welcome as always :-)

5 comments:

  1. Can someone please explain what the CICE protected(lock) syntax does above and beyond what synchronized(lock)? They sound like they do the same to me (i.e. lock before executing the block, unlock after exiting it).

    ReplyDelete
  2. Dimitris Andreou4 January 2008 at 11:08

    Gili, "lock" is a Lock, not an object used as a monitor.

    ReplyDelete
  3. Could one write an onEDT control structure with FCM or BGGA?

    Like this :

    internalData = makeSomeLongIOCall();
    onEDT(){
    fireDataChangedListeners();
    }

    That would clean up my code a lot.

    ReplyDelete
  4. Stephen Colebourne7 January 2008 at 23:38

    @Colin, BGGA encourages code like yours, JCA will allow it, but discourages it. The reason is that you need to consider what happens if return is called within the onEDT block - it won't be what you want.

    ReplyDelete
  5. I look forward to having ARM blocks in the java language. It's a simple and straightforward solution to a very common problem I have (sql statements to be closed).
    All others seriously lack of readability.
    Is there a place where we can vote for these RFE ?

    ReplyDelete