Monday, July 27, 2009

Closures in Java: another proposal

I am glad that none of the proposals for Closures have been chosen for Java 7. If you don't know, their code names are: BGGA, CICE, and FCM. Why? Because they all suck. Here is my proposal: to introduce alternative syntax for defining an anonymous inner class that extends Object and implements an interface with only one method, with the following construct: { arg1, arg2,... argN => statements; } or { statements; } if the method doesn't have any parameters.

Example 1:
SwingUtilities.invokeLater({field.setText("Hello");})

which is equivalent to:
SwingUtilities.invokeLater(new Runnable() {
public void run(){
field.setText("Hello");
}
});


Example 2:
List<String> list = ...;
Collections.sort(list, {a, b => return a.compareToIgnoreCase(b);});

which is equivalent to:
List<String> list = ...;
Collections.sort(list, new Comparator<String, String>() {
public int compare(String a, String b) {
return a.compareToIgnoreCase(b);
}
});

11 Comments:

At July 27, 2009 at 6:27 PM , Blogger Neal Gafter said...

Your specification leaves out almost all the important details of a language specification. When passed as an argument to a method, how does the compiler perform overload resolution to select the called method? How does it infer the (untyped) closure parameter types? Which of these is performed before the other?

 
At July 28, 2009 at 12:00 AM , Blogger Roger Wilco said...

1. "When passed as an argument to a method, how does the compiler perform overload resolution to select the called method?"

Very easy - by the type of the argument. There is no need to perform any "overload resolution" because in my proposal, the type of the argument must be an interface with one method.

2. "How does it infer the (untyped) closure parameter types?"
Closure parameters are simply the names of the corresponding method parameters which have their types defined in the interface.

 
At July 28, 2009 at 12:04 AM , Blogger Neal Gafter said...

@Alex - the type of *what* argument? If the name of the method being called is overloaded, you have to decide which overload you're taking the type of the argument from. Are you proposing that closures can't be used in arguments to methods that are overloaded?

 
At July 28, 2009 at 9:10 AM , Blogger Roger Wilco said...

Oh, thanks for pointing that out, Neal. I didn't think about overloading in that context. Ok, in case if there is ambiguity we could add an explicit typecast just before the closure, much like you would before a null that you pass to an overloaded method.

For example, you have two methods in class X: call(Callable<T>) and call(Runnable). Then invoking call(Runnable) method with a closure parameter would look like this:

xxx.call((Runnable){doSomething;});

 
At July 28, 2009 at 9:15 AM , Blogger Neal Gafter said...

@Alex: I didn't ask how a programmer would "work around" an ambiguity, I asked what happens (in the absence of a cast). Is it an error? What exactly is the error? (Your "specification" doesn't appear to describe any possible errors)

 
At July 28, 2009 at 10:56 AM , Blogger Roger Wilco said...

In the absence of a typecast, there will be a compile-time error.

Example:
xxx.call({doSomething();})

will generate the following error:
reference to call is ambiguous, both method <T>call(java.util.concurrent.Callable<T>) and method call(java.lang.Runnable) match

 
At July 28, 2009 at 11:31 AM , Blogger Neal Gafter said...

What are the rules for deciding which ones "match"? Presumably the "body" of the "closure" might match some types and not match other types. So that are the rules?

Also, some of the methods in the overload set might be generic - that is, they might have type parameters - and those type parameters might be used as part of the declaration of the method parameter that is to "match" the closure. How do you decide, in that case, whether or not the closure "matches"?

 
At July 28, 2009 at 1:17 PM , Blogger Roger Wilco said...

1. "What are the rules for deciding which ones match?"

Method parameter will be considered a match if its type is an interface with one method. If there is more than one matching method then a compile time error described above will be generated.

The compiler won't attempt to resolve method signature based on the number (or types) closure parameters. This part is debatable but I don't consider it an issue of principle.

2. "Also, some of the methods in the overload set might be generic ... How do you decide, in that case, whether or not the closure "matches"?

If the interface is generic, an explicit typecast is needed, too.

Example:
xxx.call((Callable<String>){return "Hello";});

 
At July 28, 2009 at 1:21 PM , Blogger Roger Wilco said...

Another possible use of a closure is in an assignment:

Callable<String> callback = {return "Hello";};

 
At July 28, 2009 at 1:54 PM , Blogger Roger Wilco said...

I just discovered that my "proposal" is actualy CICE! What a shame.

 
At July 28, 2009 at 1:57 PM , Blogger Roger Wilco said...

OK, it's a little different than CICE because you don't have to use class declaration in many cases which makes it more elegant and that's what the purpose of the closures is -- to make certain code constructs more elegant.

 

Post a Comment

Subscribe to Post Comments [Atom]

<< Home