The Source for Java Technology Collaboration
User: Password:



Rémi Forax's Blog

July 2008 Archives


Alternative syntax for BGGA closure

Posted by forax on July 07, 2008 at 06:12 AM | Permalink | Comments (17)

In my last blog entry, i've said it was time to discuss about the BGGA closure syntax. So here is my proposed syntax.

The closure syntax

There are two parts in a closure syntax, the first one defines the closure type, the second one defines the expression (not a statement) of the closure. By example, with a BGGA closure { int x => x+2 }, the expression part is x+2. BGGA uses a trick for closure type, it infers the return type using the result of the expression so the closure type is a function that takes an integer and returns an integer.

Why BGGA syntax is not great

In my opinion for multiple reasons:

  1. Closure is delimited by curly braces, but in Java curly braces is used for defining blocks and not expressions.
  2. In Java unlike Scala function return type is defined before the parameter types but BGGA function type define return type after parameter types {char,char => boolean}.
  3. As already said => is too close from <= (less than equals).
  4. Mixing parenthesis and curly braces is hard to read, anonymous class already do that and it's a pain point for beginners (and IDE :)
    new Thread({=> System.println(); }).start()
  5. Curly braces are already used as expression to initialize arrays or annotation values.
  6. Function type syntax {int,int=>int} is too close from closure one {int x,int y=>x+2}; but these things are different concepts.
  7. { int => int } and { int ==> int } means to different things but syntaxes are too similar and i'm not able (yes really) to recall wich one defines restricted closure.
  8. BGGA closures are defined as instructions followed by an expression, so you can write
             { String s, int n => String r = ""; for ( ; n > 0; n--) { r += s; } r };
           
    which is equivalent to this code
      static String concat(String s, int n) {
        String r = "";
        for ( ; n > 0; n--) {
          r += s;
        }
        return r;
      }
      ...
      #concat(String,int)
           
    Guess which one is more readable. In my opinion, closure should be restricted to an expression.

Ok, enough critics. Let's be constructive.

My proposed syntax

  expression closure = closure_parameters  expr
  closure_parameters = '|' parameters lists '|'

Examples:

   RemisClosure = |int a, int b| a + b;
   
   Integer[] primes = { 19, 23, 2, 11, 17, 31, 5, 13 };
   Arrays.sort(primes, |Integer x, Integer y| y.compareTo(x));

The syntax is close to Ruby's one, as I previously said, closures are expressions because in Java, x+y is an expression, no need to add parenthesis, brackets or curly braces. Parameter types are enclosed by pipe ('|') to avoid confusion with other braces.

Closure in other languages:

  BGGAClosure       = {int a, int b => a + b };
  GroovyLikeClosure = {int x, int b -> a + b };
  RubyLikeClosure   = { |int a, int b| a + b };
  FanClosure       := |int a, int b -> int| { return a + b }

Gotcha: closure with no parameter type

In Java void is not a parameter type, so |void| 2+3 is not a valid syntax for a closure that takes no argument and return an int.
I see two syntaxes:

  1. || 2+3, even if there is not clash with boolean or, it could confuse beginner.
  2. 2+3 this means there is an automatic conversion from an expression to closure. It adds another pass to the overload resolution algorithm, which is already complex because it handles boxing, varags and generics.
I am not able to decide between the two syntaxes, so this remains an open issue.

Function type

In Java, return type is declared before parameter type, I don't see why function type should break that rule. So a function type is a return type followed by parameter types separated by comma and enclosed in parenthesis. This syntax is very similar to FCM one.

Update ask by Neal, grammar of function type:

  return_type '(' parameter_type_list? ')'

Example:

  f(2, |int x| x+2);
  ...
  static int f(int value, int(int) closure) {
    return closure.invoke(value);
  }
  
  g(|int x| |int y| x+y);
  ...
  static int g(int (int ()) closure) {
    return closure.invoke(2).invoke(3);
  }

  int (char[]) throws IOException read =
    reader#read(char[]);

  int lengthSum=sum(|String s| s.length(), "toto", "tutu");
  ...
  static <V> int sum(int (V) f, V... values) {
    int sum=0;
    for(V value:values) {
      sum+=f.invoke(value);
    }
    return sum;
  }

Closure block

A closure block is a block of instructions that can be passed to a method in order to perform control abstraction.
A closure block is declared like this:

  expr = method_call closure_parameters? block_body

Example:

   Reader reader=...
   with(reader) {
     ...
   }
   ...
   public static void with(Closeable closeable) void f() {
     try {
       f.invoke();
     } finally {
       reader.close();
     }
   }

This syntax declares the closure block type after the method parameters (like in Scala). The syntax has to be different from a method that takes a function type as parameter because

  • closure block let you use return, break, continue.
  • a method that takes a function block performs exception transparency transparently.
  • a closure block type is not a real type, it cannot appear somewhere else.
A little more formally, methods that take a closure block are declared like that:

  method = method_decl closure_block_decl* method_body

  closure_block_decl = name '(' parameters ')'

Closure block return type is always 'void', so the syntax ommit it. The variable containing the closure block is not useable as left value.
Examples

  static void forEach(int... nums) block(int) {
    for (int n: nums) {
      block.invoke(n);
    }
  }
  ...
  int sum = 0;
  forEach(2,3,4) |int n| {
    sum += n;
  }
  ...
  static boolean hasNegativeValue(int... nums) {
    forEach(nums) |int n| {
      if (n<0)
        return true;
    }
    return false;
  }

I don't think a specific syntax for loop abstraction worth the need to learn a new syntax.

 void forEachEntry(Map<K,V> map) block(K,V) {
    for(Map.Entry<K,V> entry : map.entrySet()) {
        block.invoke(entry.getKey(), entry.getValue());
    }
}
...
Map<String, String> map =
forEachEntry(map.entrySet()) |String first, String second| {
  ...
}

Conversion between function type and block type

It's possible to pass a function type as argument of method that declares a closure block type. The opposite is not true because closure block can do non local transfer but you can use a cast.

  void invokeInEDT() closure() {
    void() f=closure;          // illegal conversion
    final void() f=((void())closure; // ok

    EventQueue.invokeAndWait(new Runnable() {
      public void run() {
        f.invoke();
      }
    });
  }

This entry is already too long, so i stop here. I wait your comments.
Rémi



Source of BGGA prototype available

Posted by forax on July 05, 2008 at 03:37 AM | Permalink | Comments (3)

After being frozen more than 3 months, the BGGA closure dev is resumed, the sources of the BGGA prototype have been pushed 3 hours ago by Neal Gafter.
The sources are available in the closure workspace: http://hg.openjdk.java.net/closures/closures/langtools/ .

Troll: I think it's time to reconsider the syntax to be less alien or from mars.





Powered by
Movable Type 3.01D
 Feed java.net RSS Feeds