Monday, January 7, 2008

Completion Transparency

Completion Transparency

Completion transparency enables us to create methods that transparently return the result of a closure which is passed as argument. For example:

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

We can pass to the withLock method a block that returns any value. For example:

  Lock lock = new ReentrantLock();
  Integer i = withLock(lock, { => Integer.valueOf(42) });
  

Due to completion transparency, we can also use a block that does not return anything (i.e. a block of type { => void }):

  withLock(lock, { => System.out.println("mining..."); });
  

And even a block that cannot complete normally, i.e. throws exception (in this case only unchecked exceptions are allowed):

  withLock(lock, { => throw new NullPointerException(); });
  

To allow checked exceptions as well, we can use exceptions transparency:

  static <T, throws E> T withLockEx(Lock lock, { => T throws E} block) 
    throws E {
    lock.lock();
    try {
      return block.invoke();
    } finally {
      lock.unlock();
    }
  }
  
  public static void main(String[] args) {
    try {
      Lock lock = new ReentrantLock();
      withLockEx(lock, { => throw new Exception(); });
    } catch (Exception e) { e.printStackTrace(); }
  }
  

Implementation issue

Let us recall that each closure is converted to an instance of interface. For example, a closure { => System.out.println("buff"); } is converted to an instance of V:

  public interface V {
    void invoke();
  }
  

To achieve completion transparency, the compiler is allowed to use Void instead of void. For example, the code

  withLock(lock, { => System.out.println("miaow"); });
  

will be transformed approximately as follows:

  withLock(lock, new javax.lang.function.O() {
    public Void invoke() {
      System.out.println("miaow");
      return null;
    }
  }
  

In case of closures that do not complete normally, the invoke method returns java.lang.Unreachable which is a special class that is considered a subclass of all classes. This means that an Unreachable may be assigned to any reference variable. For example, the statement

  Integer i = { => throw new NullPointerException(); }.invoke();
  

will be transformed roughly as follows:

  Integer i = new javax.lang.function.O() {
    public Unreachable invoke() { throw new NullPointerException(); }
  }.invoke();