Blocks (closures) in Java and C
Update 2014: 12 years since we were exploring this idea, Java 8 was released and it finally supports lambda functions.
Looking back, I prefer the elegance of how it was proposed here, but nonetheless this is game changing for Java. It took a looong time due to disagreements in the community about the prefered syntax/implementation.
Embarrassingly, even C++11 beat Java to it by 3 years.
James Strachan talks about some syntactical sugar enhancements that can be made to Java borrowed from C#. Although it sounds petty, this stuff really makes a difference when you’re staring at code all day. Clean and readable code makes for relaxation which makes for smarter thinking. Syntax is closely related to karma.The foreach and using operators are particularly nice as it provides a cleaner sequence for commonly executed sequences of events. But why stop there?
One of the features that made Smalltalk and Ruby so popular was the use of blocks. These allow you to create your own constructs.Suppose blocks were added to Java/C#. We could create our own constructs. We wouldn’t be limited to what the language designers thought we may need.
Here’s an example of how a foreach facility could be added to Java/C#.
public class List {
...
public block void foreach() {
for(int i = 0; i < internalArray.length; i++) {
Object item = internalArray[i];
yield(item); <//> execute block
}
}
}
Usage:
list.foreach(Person person) {
print(person.getName());
}
Okay, that’s foreach added.How about something similar to using that ensures a resource is always closed properly. Let’s add some custom transaction management for fun.
public class Database {
...
public block void using() {
Connection con = ...; // open connection
Transaction tran = con.beginTransaction(); // start transaction
try {
yield(con); // execute block
tran.commit(); // commit
} catch (Throwable error) {
tran.rollback(); // rollback if error
throw error;
} finally {
con.close(); // always close con
}
}
}
Usage:
database.using(Connection connection) {
connection.execute("UPDATE blah");
connection.execute("DELETE xxxx");
}
Let’s add something to easily scroll through a result set:
public class Connection {
...
public block void query(String sql) {
ResultSet rs = getResultSet(sql); // open results
try {
while(rs.next()) { // iterate through results
yield(rs.get(1), rs.get(2)); // execute block
}
} finally {
rs.close(); // close results
}
}
}
Usage:
int totalAge = 0;
connection.query(String name, int age; "SELECT name, age FROM people") {
print("Name : " + name);
totalAge += age;
}
This is a sample of the syntax I’m suggesting - I’m sure it can be improved:
public class Test {
public block int myBlock(int arg) {
number = arg2 + 1;
print(".start block");
yield(n);
yield(n);
print(".end block");
}
}
// usage
int result = test.myBlock(int number; 4) {
print("..start custom");
print("..." + number);
print("..end custom");
number += 2;
}
print("result: " + result);
Output:
.start block
..start custom
...5
..end custom
..start custom
...7
..end custom
.end block
result = 9
Blocks are similar to anonymous inner classes, but offer two distinct advantages:
- The calling syntax is much simpler. Imagine using an inner class to iterate over a collection! Or imagine how ugly it would be have to use an inner class to apply a compartor to a sorting algortithm or if you wanted to use the visitor pattern. Oh wait, we do that don’t we :).
- Scoping. Blocks have access to values available to the code calling the block and values inside the block.
Forget adding a couple of keyword extensions - add the ability to create our own extensions. Thanks, Steve Freeman, for the conversation about this last night.