Overview
There has been some discussion about adding closures to Java 7. Are they just Syntactic Sugar or do they serve a real need?
Closures appear to be useful for those who naturally program in a functional way, they should also encourage a more flexible way of programming.
What I have been looking for is examples where the use of closures are either simpler or faster than using standard Java.
So what is the power of closures?
An often quoted statement about closures in Java is
I think ... that in another thirty years people will laugh at anyone who tries to invent a language without closures
Obviously you don't want to be laughed at, but is this the only reason to include closures?
Neal Gafter puts forward a more compelling reason for Closures here
... closures ...allow you to write methods that are part of APIs that act like control statements in the language.
He gives an example,
withLock is a method that I hope would be added to the concurrent framework. It basically looks just like a synchronized statement. You say, withLock, open-paren, then a lock variable, close paren, open curly brace, then a bunch of statements, and then close curly brace. It looks just a like a built-in statement form, but it's a method invocation, an invocation of this withLock method
How would withLock work?
This is how you might use a Java 5 lock today.
private final Lock myLock; // in code myLock.lock(); try { // do something while locked. } finally { myLock.unlock(); }
This is how you could apply the lock around a closure, simplifying the code and hiding the details of how the lock works. e.g. if you forget the finally block you may find your application fails to recover after an exception.
private final Lock myLock; // in code withLock(myLock) { // do something while locked. }
A note on measuring complexity
There are many definitions of what is simpler or more complex. For the purpose of this discussion I will look at the number of non-letter symbols required. In a concession to Java I will not count the comma at the end on a line or the dot operator (as this is a feature of an OO language and closures won't make a difference here). I count character combinations as one symbol e.g. => () {} []
Other examples
The following examples I find less convincing for reasons I will explain. However once Closures are available they can be used is a variety of different ways.
Comparison with a Scala example.
Here is a Scala example of a client server application which sends a filter function via serialisation. random.scala (123 symbols, 92 lines) For more examples see Scala Examples
I have rewritten this class with simplicity in mind. SimpleExample.java (85 symbols, 63 lines) with a unit test of SimpleExampleTest.java
You will note it doesn't bother to send the filter. In the tests it get 30,000 correct values/second.
So what would have happened if we had send the filter to the server FunctionalExample.java (105 symbols, 83 lines) and FunctionalExampleTest.java
This follows the Scala example more closely, but is altered so it can function as a performance test. i.e. no sleep()ing.
In tests, it gets 6,000 correct values/second.
So what happened? The functional example should be more efficient you say, it is sending less data over the network.
The difference is the SimpleExample example uses BufferedOutputStream, a simple change which alters just one line, which makes far more difference than all the extra code added.
Conclusion
In this example, using closures added lines of code and symbol complexity compared with the simplest implementation. There was an apparent optimisation achieved by the use of a closure, however it turned out to be 5x slower than altering one line to buffer the output of the server.
Post Script.
Buffering the output for the Functional example brings the performance up to 30K/second as well.
Closures in mathematical calculations.
In the following example is based on an example here (38 symbols)
function derivative(f, dx) {
return function(x) {
return (f(x + dx) - f(x - dx)) / (2*dx);
};
}
function inverse(f) {
return function(x) {
var c;
var guess;
var df = derivative(f, delta);
for(c = 0; c < maxIterations; c++) {
var delta = (f(guess) - x) / df(guess);
guess -= delta;
if (abs(delta) < precision)
return guess;
}
// failed.
return NaN;
}
}
would be written in Java as a class say NewtonRaphson (33 symbols)
public abstract double function(double x); public double derivative(double x) { return (function(x + delta) - function(x - delta)) / (2 * delta); } public double inverse(double x) { double guess = x; for (int i = 0; i < maxIterations; i++) { double f_x = function(guess) - x; double f_dx = derivative(guess); double delta = f_x / f_dx; guess -= delta; if (abs(delta) < precision) return guess; } return NaN; }
Poor examples of closures.
1) I don't consider this a good example (10 symbols)
public static void main(String[] args) { int plus2(int x) { return x+2; } int(int) plus2b = plus2; System.out.println(plus2b(2)); }
here as it could be written without a closure (5 symbols)
public static void main(String[] args) { System.out.println(2 + 2); }
2) Another example here (11 symbols)
public static void printMap(Map<String,Book> m) { forEach(m, {String key, Book book => System.out.println(key + " = " + book); }); }
could be written as the following (11 symbols)
public static void printMap(Map<String, Book> m) { for(Entry e: m.entrySet()) System.out.println(e.getKey() + " = " + e.getValue()); }
or even the shorter version, (assuming comma-space doesn't appear in you data) (9 symbols)
public static void printMap(Map<String, Book> m) { System.out.println(("" + m).replaceAll(", ", "\n")); }
3) In this example, a file is read by line and lines matching an regex are printed. here (6 symbols)
def File.grep(fileName, pattern)
IO.foreach(fileName) do |line|
if md = pattern.match(line)
yield md;
end
end
end
However this could also be written this way (9 symbols)
public static void grep(String filename, String regex) { for(String line: readByLine(filename)) if (line.matches(regex)) System.out.println(line); } // where the following method returns an Iterable wrapper for a BufferedReader public static Iterable<String> readByLine(String filename);
The body of the method is almost the same length. The extra symbols come from the the brackets on for, if and println and closures wouldn't help with these.
4) This example in ruby (24 symbols)
tax = 0.08
prices = [4.45, 6.34, 3.78]
tax_table = prices.collect {|price| {:price => price, :tax => price * tax}}
tax_table.collect {|item| puts "Price: #{item[:price]} Tax: #{item[:tax]}"}
could be written in Java (12 symbols)
double tax = 0.8; double[] prices = {4.45, 6.34, 3.78}; for(double price: prices) System.out.println("Price: " + price + " Tax: " + price * tax);
Resources
Closures for the Java Programming Language, a proposal by Gilad Bracha, Neal Gafter, James Gosling, and Peter von der Ahé:
http://www.javac.info/
Closures for Java, a webcast of Neal Gafter's JavaPolis talk that he gave just before this interview:
http://www.bejug.org/confluenceBeJUG/display/PARLEYS/Closures+for+Java
Neal Gafter's blog, in which he discusses the closures proposal:
http://gafter.blogspot.com/
Blog's on Closure's in Java 7.
http://tech.puredanger.com/java7#closures
Add Comment