Tuesday, August 19, 2008

Modifier 'for'

Modifier 'for'

Method modifier 'for' enables us to declare and call "loop-like" methods.

  static for void eachEntry(int[] values, { int ==> void } block) {
    for (int p : values) {
      block.invoke(p);
    }
  }
  
  public static void main(String[] args) {
    int[] v = { 2, 3, 5, 7, 11 };
    for eachEntry(int i : v) {
      System.out.println(i);
      if (i > 3) {
        break;
      }
    }
  }
  

For example, we can declare a method that iterates through a map:

  static for <K, V> void eachMapEntry(Map<K, V> dict, 
      { K, V ==> void } block) {
    for (Map.Entry<K, V> entry : dict.entrySet()) {
      block.invoke(entry.getKey(), entry.getValue());
    }
  }
  
  public static void main(String[] args) {
    Map<String, String> dict = new HashMap<String, String>();
    ...
    for eachMapEntry(String k, String v : dict) {
      System.out.println(k + ": " + v);
    }
  }
  

Nonlocal transfer

Nonlocal transfer

In unrestricted closures, we can use the break, continue, and return statements.

  for (int i = 0; i < 5; i++) {
    System.out.println(i);
    if (i == 2) { ==> break; }.invoke();
  }
  

These statements are always lexically bound, i.e. they are bound in the context in which the closure is declared (as opposite to invoked). So, the following code will print numbers from <1,10> that are not multiples of 3.

  static void m(int i, { int => boolean } cond, { ==> void } block) {
    if (cond.invoke(i)) {
      block.invoke();
    }
  }
  
  public static void main(String[] args) {
    for (int i = 1; i < 10; i++) {
      // continue is bound to the for loop
      m(i, { int i => i % 3 == 0 }, { ==> continue; });
      System.out.println(i);
    }
  }
  

The return statement returns from the bound method.

  static int m1() {
    // return here means "return from m1"
    m2({ double d ==> if (d < 0.5) return 1; });
    return 0;
  }
  
  static void m2({ double ==> void } p) {
    // the closure invocation can cause return from m1
    p.invoke(Math.random());
  }
  

If the bound method is not active at the time when we call the closure with the return statement, we will get the UnmatchedTransfer exception. For example:

  // any type can be used as the return type of p in this
  // example because the closure always returns earlier
  // so, p can be declared also as static { ==> int } p;
  static { ==> boolean } p;
  
  static int m1() {
    p = { ==> return 1; };
    return 0;
  }
  
  public static void main(String[] args) {
    System.out.println(m1());
    System.out.println(p.invoke());
  }
  

Unrestricted closures are useful when declaring "statement-like" methods.

  static void withLock(Lock lock, { ==> void } block) {
    try {
      lock.lock();
      block.invoke();
    } finally {
      lock.unlock();
    }
  }
  
  public static void main(String[] args) {
    Lock guard = new ReentrantLock();
    double d = Math.random();
    withLock(guard) {
      if (d < 0.5) {
        return;
      }
      ...
    }
  }