Monday, May 28, 2007

JavaOne 2007 - Closures for the Java Language - TS-2294 and BOF-2358 Notes

NOTE: This is an aggregation of the notes I took from Neal Gafter's two sessions at JavaOne 2007 on his Closures for the Java Language proposal.

"Why add closures? Isn't Java complicated enough already?"
“ In another thirty years people will laugh at anyone who tries to invent a language without closures, just as they'll laugh now at anyone who tries to invent a language without recursion.” — Mark Jason Dominus

Goals of the Closures Spec Proposal
  • Provide concise "function" literals - without the pain of anonymous instances (i.e. anonymous inner classes)
  • Interoperate with existing APIs (esp. java.util Collections)
  • Solve problems which Anonymous Inner Classes can't - simplify
  • Enable control APIs (c.f. Scala, Ruby)
  • Wrapped code not changed
  • Function and aggregate operations
  • Write methods which act like new keywords (but only act like)
  • Simple but powerful
Definitions
  • A "closure" is a function which refers to free variables in its lexical context where a "function" is a block of code with parameters that may produce a result value.
  • A "free variable" is an identifier used but not defined by the closure.
  • "Control APIs" are API specific statement forms on a par with built in statements, like special methods.
Problems with anonymous instances
  • Verbose, extra wordy, clumsy, redundant - whereas closures are concise
  • They incompletely capture the lexical context - i.e. "this" and "toString( )" don't refer to what they ought; return, break and continue operate differently also, and you cannot use non final local variables
  • Consequently they force irrelevant refactoring
  • Control APIs are not possible - You cannot wrap arbitrary blocks of code
Specification
Syntax - Closure Expressions
{int x => x+1}
{int x, int y => x+y}
{String x => Integer.parseInt(x)}
{=> System.out.println(“hello”);}
Primary:
Closure
Closure:
{ FormalParameterDecls opt => BlockStatements opt Expression opt }
  • Creates an object which represents code of the body and the lexical context
  • Few restrictions - may access local variables and this and may return from the enclosing method
The closure conversion turns a closure into an instance of some interface
  • This provides interoperability with existing APIS
  • Means you can restrict the closures operations (but you must do this explicitly. It is not done for you by default)
  • If there is not target type, it will use the natural function type
Syntax - Function Types
{int => int}
{int, int => int}
{String => int throws NumberFormatException}
{ => void}
{T => U}
Type:
{TypeList opt => Type FunctionThrows opt }

{String => int throws NumberFormatException} is shorthand for java.lang.function.IO where
package java.lang.function;
public interface IO {
int invoke(A a0) throws X;
}
  • Function types are "ordinary" interface types with an invoke method
  • You can extend and implement them
  • They can declare variables, parameters, return types etc.
  • (There are no special type rules)
Syntax - Control Statements
withLock(lock, {=>
doSomething();
});
withLock(lock) {
doSomething();
}
ControlStatement:
for opt Primary ( Formals : ExpressionList opt ) Statement
for opt Primary ( ExpressionList opt ) Statement

Is translated to:
Primary( ExpressionList, { Formals => Statement });

Examples
1. Control APIs - Perform some operation while holding a java.util.concurrent.Lock
(a) Today:
void incrementBalance(int deposit) {
myLock.lock();
try {
balance += deposit;
} finally {
myLock.unlock();
}
}
(b) Using a proposed new closure-based API:
void incrementBalance(int deposit) {
Locks.withLock(myLock,
{ => balance += deposit; });
}
(c) Using the control statement syntax:
void incrementBalance(int deposit) {
Locks.withLock(myLock) {
balance += deposit;
}
}
2. Aggregate Operations - Make a new list by applying a function to each element of an existing list:
List parseInts(List strings)
throws NumberFormatException {
return Collections.map(
strings, {String s => Integer.decode(s)});
}
3. Interaction with existing APIs - launch a task using an executor
(a) Today:
void launch(Executor ex) {
ex.execute(new Runnable() {
public void run() {
doSomething();
}
});
}
(b) Useing a closure:
void launch(Executor ex) {
ex.execute({ =>
doSomething();
});
}
(c) Using the control statement syntax:
void launch(Executor ex) {
ex.execute() {
doSomething();
}
}
3. Interaction with existing APIs - Add a Swing listener
(a) Today:
void addListener(final ItemSelectable is) {
is.addItemListener(
new ItemListener() {
public void itemStateChanged(ItemEvent e)
{ doSomething(e, is); }
}
);
}
(b) Using a closure:
void addListener(final ItemSelectable is) {
is.addItemListener(
{ ItemEvent e => doSomething(e, is); }
);
}
(c) Using the control statement syntax:
void addListener(final ItemSelectable is) {
is.addItemListener(ItemEvent e :) {
doSomething(e, is);
}
}
New APIs
  • API specific control constructs - Collections (e.g. Map Specific iteration), concurrency (e.g. locking) and closables (e.g. resource streams closed automatically at the end of a block)
  • Aggregate operations. E.g.:
List list = …;
Integer sum = Collections.reduce(
list,
{Integer x, Integer y => x+y});
  • Functional primitives

Questions from the Audience
"How do you deal with the namespace issue?"
  • A closure is a method name therefore you have your own scope
  • Use an import statement - a closure is just an API method
"Have you thought of restricting the use of closures? i.e. a member variable which is a closure which extends a generic"
  • No, no one can determine what is and what isn't useful. Just because you can't think of a use case, doesn't mean someone else won't. The only reason you would do something is because you need to.
  • The proposal includes ways for all API writers to restrict how closures are used with them, but blanket restrictions would be trouble.
"How do closures get executed?"
  • The closure expression has parameter types, a return type, and types it uses within it. The compiler analyses this but does not go and impose this on the closure.
  • It works like overloader resolution - it picks which is most specific, therefore there is no type inference but there is a compatability rule.
"How do I pass multiple parameters?"
  • You don't need to. Everything outside the closure can be accessed from within the closure.
"How do we evaluate whether we should add 'this'? - and will it make Java simpler?"
  • It will make it easier to write and read. ("I'm hoping for a video with a Star Wars theme to help me decide") Java today is not the easy Java we had in 1999
Where Can I Read / Hear More?

No comments: