Richard has posted 1 posts at DZone. View Full User Profile

Comparing Closures in Java, Groovy and Scala

06.16.2008
| 11628 views |
  • submit to reddit

On Paul's return from JavaOne this year, we spoke about Neal Gafter's Closures Cookbook talk. From what I understood, this was a look at the BGGA closures proposal, and contained an example that pushed hard on some of the tougher closure issues for Java. I thought it might be fun to look at the Java example from the talk, and covert it to Scala and Groovy.

Why those languages? Because they are the three JVM languages I'm most interested in. I suppose I could also have compared the closure support in Jython, JRuby or... well, there are a few to choose from, but this blog is going to be plenty long enough with just three.

Let's start with the Java example that was given, remembering that this is a proposed syntax, that may or may not make it to Java 7 or later. As I understood the example it was this: imagine you want to add the ability to time a block of code, and you wanted to do it in a way that would look almost like a new keyword has been added to the language; and you wanted to pass in a parameter to name what you were timing; and the block you're timing returns a result, or might throw an exception. So, quite an involved case.

BGGA Proposed Syntax

Here's how the current Java proposal looks:

// Here's a method that uses the time call:

int f() throws MyException {
time("opName", {=>
// some statements that can throw MyException
});
time("opName", {=>
return ...compute result...;
});
}

So we're timing a couple of operations, and we're doing this inside a method, f, that returns an integer. The implementation would be....

interface Block<R, throws X> {
R execute() throws X;
}

public <R, throws X> R time(
String opName, Block<R, X> block) throws X {
long startTime = System.nanoTime();
boolean success = true;
try {
return block.execute();
} catch (final Throwable ex) {
success = false;
throw ex;
} finally {
recordTiming(
"opName", System.nanoTime() - startTime, success);
}
}

As you can see, time takes an arbitrary text label and a block of code, runs the block and tells you how long the block took to run and if it succeeded or not.

That's the example that was given at JavaOne. Now for the same thing in Groovy...

Same example in Groovy

To make runnable code for Groovy (and for Scala), I had to decided to time something. So I'm timing a block of code that randomly throws an exception or returns something. And then timing a block of code that just returns a number. In Groovy that would be:

def time(opname, block)
{
long start_time = System.nanoTime()
boolean success = true
try {
return block()
} catch (Throwable ex) {
success = false;
throw ex
} finally {
diff = System.nanoTime() - start_time
println "$opname $diff $success"
}
}

int f() throws Exception {
time("a") {
Random r = new Random()
if (r.nextInt(100) > 50)
throw new IOException("Boom")
else
return 42
}

time("b") {
return 7
}
}

println f()

An example of running the code, showing one run where no exception was thrown, and operation "a" and "b" where both successfully and took a certain amount of time....

$ groovy time.groovy 
a 37116000 true
b 69000 true
7

...and an example where an exception was thrown when timing operation "a":

$ groovy time.groovy 
a 39998000 false
Caught: java.io.IOException: Boom
at time$_f_closure1.doCall(time.groovy:21)
at time$_f_closure1.doCall(time.groovy)
at time.time(time.groovy:6)
at time.f(time.groovy:18)
at time.run(time.groovy:31)
at time.main(time.groovy)

Note that the Java example is typed in that it uses a generic type, R, for the return value which gives you some compile-time checks. That is, when you run time() and use the result, the compiler will enforce that your declaration of the result has the same type as the return type of the block you're timing.

Although Groovy does support generics, I've not used them here, and as a result the Groovy example doesn't have that type-safety. I think that's the way one would typically write Groovy code.

Same example in Scala

Now a look at the same code in Scala:

def time[R](opname: String)(block: => R) = {
val start_time = System.nanoTime()
var success = true
try {
block
} catch {
case ex: Throwable => {
success = false;
throw ex
}
} finally {
val diff = System.nanoTime() - start_time
println(opname + " " + diff + " "+success)
}
}

def f():Integer = {

val answer = time("a") {
val r = new Random()
if (r.nextInt(100) > 50)
throw new java.io.IOException("Boom")
else
"42"
}

println ("the answer, "+answer+" is of type "+
answer.getClass())

val seven:Integer = time("b") {
7
}

println ("seven is of type "+seven.getClass())

return seven
}

f()

I'm still not using Scala day-to-day, so this might be a little awkward: thank you to the London Scala User Group for helping me clean up my syntax, but all the mistakes are mine.

This code has the same properties as the Java code (type safety via the R generic type), but seems a little shorter and neater. Additionally, the thing I like about the Scala code (and the Groovy code) is that the languages return the value of the last statement in a block, and that the syntax allows a clean time("thing") { ... } format.

One observation: I've used the Integer class, which is deprecated, in order to be able to print out the class of the return types in the function f(). Without the :Integer declaration I was getting weird compile errors. As I said, my understanding of Scala and type inference isn't there yet.

The output from running the code:

a 913000 true
the answer, 42 is of type class java.lang.String
b 13000 true
seven is of type class java.lang.Integer

...and an example where the method threw an exception:

a 936000 false
java.io.IOException: Boom
 at Main$$anonfun$1.apply((virtual file):26)
 at Main$$anonfun$1.apply((virtual file):23)
 at Main$.time$1((virtual file):8)
 at Main$.f$1((virtual file):23)
 at Main$.main((virtual file):44)
 at Main.main((virtual file))
// rest of stack trace removed
 

Conclusions

There's no conclusions here. It's just an exercise in comparing closures code in three different languages. I've probably missed some of the nuances of the Java example, but hey... it's a starting point.

Original Blog Post: http://blog.spiralarm.com/richard/2008/06/comparing-closures-in-java-groovy-and.html

 

Published at DZone with permission of its author, Richard Dallaway.

(Note: Opinions expressed in this article and its replies are the opinions of their respective authors and not those of DZone, Inc.)

Comments

Neal Gafter replied on Sat, 2008/06/21 - 10:06am

The Groovy example does NOT have the same semantics as the Java (closures) code.  In f(), the following code

  1.   if (r.nextInt(100) > 50)  
  2.    throw new IOException("Boom")  
  3.   else  
  4.    return 42

should cause the following statement of the enclosing method NOT to be executed (whether throwing an exception out of the current method or returning from it, the following statements in the method are not executed).  The fact that Groovy goes ahead and executes following statements illustrates that Groovy simply cannot be used to solve the same problems in the same way as BGGA.

Richard Dallaway replied on Wed, 2008/06/25 - 12:01am

Many thanks for pointing that out.

For me, the gotcha is that under BGGA the return statement in the closure acts as if the closure "wrapper" were omitted, causing the return to return from the enclosing method (returning from f, in the example). Must... remember...that.

For anyone interested in this topic: the Closures Cookbook presentation is available online to SDN members.

Comment viewing options

Select your preferred way to display the comments and click "Save settings" to activate your changes.