"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
- 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.
- 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
Syntax - Closure Expressions
{int x => x+1}Primary:
{int x, int y => x+y}
{String x => Integer.parseInt(x)}
{=> System.out.println(“hello”);}
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
- 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
{int => int}Type:
{int, int => int}
{String => int throws NumberFormatException}
{ => void}
{T => U}
{TypeList opt => Type FunctionThrows opt }
{String => int throws NumberFormatException} is shorthand for java.lang.function.IO
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)
withLock(lock, {=>
doSomething();
});
withLock(lock) {ControlStatement:
doSomething();
}
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) {(b) Using a proposed new closure-based API:
myLock.lock();
try {
balance += deposit;
} finally {
myLock.unlock();
}
}
void incrementBalance(int deposit) {(c) Using the control statement syntax:
Locks.withLock(myLock,
{ => balance += deposit; });
}
void incrementBalance(int deposit) {2. Aggregate Operations - Make a new list by applying a function to each element of an existing list:
Locks.withLock(myLock) {
balance += deposit;
}
}
List3. Interaction with existing APIs - launch a task using an executorparseInts(List strings)
throws NumberFormatException {
return Collections.map(
strings, {String s => Integer.decode(s)});
}
(a) Today:
void launch(Executor ex) {(b) Useing a closure:
ex.execute(new Runnable() {
public void run() {
doSomething();
}
});
}
void launch(Executor ex) {(c) Using the control statement syntax:
ex.execute({ =>
doSomething();
});
}
void launch(Executor ex) {3. Interaction with existing APIs - Add a Swing listener
ex.execute() {
doSomething();
}
}
(a) Today:
void addListener(final ItemSelectable is) {(b) Using a closure:
is.addItemListener(
new ItemListener() {
public void itemStateChanged(ItemEvent e)
{ doSomething(e, is); }
}
);
}
void addListener(final ItemSelectable is) {(c) Using the control statement syntax:
is.addItemListener(
{ ItemEvent e => doSomething(e, is); }
);
}
void addListener(final ItemSelectable is) {New APIs
is.addItemListener(ItemEvent e :) {
doSomething(e, is);
}
}
- 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.:
Listlist = …;
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
- 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.
- 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.
- You don't need to. Everything outside the closure can be accessed from within the closure.
- 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
- TS Slides
- TS Audio (to come)
- BOF Audio (to come)
- Java.net community corner podcast
- Neal's Blog
- Stephen Colebourne's blog
No comments:
Post a Comment