Monday, January 7, 2008

Usage

Usage

In this post, I express my opinion on many places. For example, when I say "it is more intuitive...", I mean "in my opinion, it is more intuitive...".

As we already mentioned, closures are transformed to anonymous inner classes at compile time. So, all we can do with closures, can be done with inner classes as well. However, the access to local variables may require a little effort. For example, the code

  int x = 4;
  { => System.out.println(++x); }.invoke();
  

can be rewritten without closures as follows:

  final int[] x = { 4 };
  new javax.lang.function.V() {
    public void invoke() {
      System.out.println(++x[0]);
    }
  }.invoke();
  

Even if closures do not increase the power of language, in many cases they are more concise and intuitive than other means. They are a natural choice when we want to parameterize an algorithm with a function. For example, sorting may be parameterized with a comparison function:

  Integer[] primes = { 19, 23, 2, 11, 17, 31, 5, 13 };
  Arrays.sort(primes, { Integer x, Integer y => y.compareTo(x) });
  

Here, it is more intuitive to use a function argument than the Comparator interface because the purpose of the argument is only to compare two values.

One should also prefer closures for predicates because predicate is a function:

  static <T> void selectAndPrint(Iterable<T> items, { T => boolean } cond) {
    for (T item: items) {
      if (cond.invoke(item)) {
        System.out.println(item);
      }
    }
  }
  
  public static void main(String[] args) {
    List<String> cities = Arrays.asList(
      "Prague", "San Francisco", "Moscow", "New York", "Paris");
    int max = ...;
    // will print cities that are not longer than max
    selectAndPrint(cities, { String s => s.length() <= max });
    char c = ...;
    // will print cities that contain character c
    selectAndPrint(cities, { String s => s.indexOf(c) >= 0 });
  }
  

Function types can also be used to describe the input of algorithm. For example, a method that returns the size of area under the function curve should have a function argument:

  static double determineFiniteIntegral(
    double from, double to, { double => double } func) {
    double area;
    //...
    return area;
  }
  

Another usage is in a method that takes a block of code as argument. For example, the execute method takes a block that should be performed in a separate thread:

  ExecutorService pool = Executors.newFixedThreadPool(5);
  pool.execute({ => System.out.println("thinking..."); });
  

Again, it is more intuitive and logical to pass a block of code than an object that implements Runnable.

Closures enable us to write methods that behave in a similar way as the foreach loop:

  static void forEach(int[] nums, { int => void } block) {
    for (int n: nums) {
      block.invoke(n);
    }
  }
  
  public static void main(String[] args) {
    int[] p = { 5, 1, 3, 4, 2 };
    int sum = 0;
    forEach(p, { int n => sum += n; }); // will sum up the numbers in array
    int min = Integer.MAX_VALUE;
    forEach(p, { int n => if (n < min) min = n; }); // will find the minimum
  }
  

For example, we can implement a method that performs an operation on each map entry:

  static <K, V> void forEachEntry(Map<K, V> map, { K, V => void } block) {
    for (Map.Entry<K, V> entry: map.entrySet()) {
      block.invoke(entry.getKey(), entry.getValue());
    }
  }
  
  public static void main(String[] args) {
    Map<Integer, String> map = new HashMap<Integer, String>();
    //...
    forEachEntry(map, { 
      Integer key, String value => 
        System.out.println(key + ": " + value); });
  }
  

We can also use closures to implement the visitor design pattern in a simple fashion:

  class Node {
    int value;
    Node left, right;
  }
  
  class Tree {
    Node root;
    //...
    void visitPreOrder({ int => void } block) {
      visitPreOrder(root, block);
    }
    void visitPreOrder(Node p, { int => void } block) {
      if (p != null) {
        block.invoke(p.value);  // first perform the operation on this node
        visitPreOrder(p.left, block);  // then on the left subtree
        visitPreOrder(p.right, block);  // and then on the right subtree
      }
    }
  }
  
  public class TreeOperations {
    public static void main(String[] args) {
      Tree tree = ...;
      List<Integer> nums = new ArrayList<Integer>();
      // will add the numbers to the list
      tree.visitPreOrder({ int v => nums.add(v); });
      //...
    }
  }
  

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();