Spiral Arm Logo

Richard's technical notes

Saturday, June 14, 2008

Comparing closures in Java, Groovy and Scala

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.

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 execute() throws X;
}

public R time(
String opName, Block 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...

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:


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

$ 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.

UPDATE: as was pointed out to me in the comments on the Java Lobby version of this blog post, this isn't the same as the Java version. In the Java version a return in the closure returns out of the enclosing block.

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 type 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

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


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.

4 Comments:

Blogger elizarov said...

The actual BGGA proporal (and Neal's talk for that matter) contains shorter example of time method usage (via a special calling convention) that actually looks like Scala and Groovy: ion {

time("opName") { ... }

And it also has a shorter way to define time method by using function types. I welcome everybody to actually read the proposal itself.

1:36 PM  
Blogger Richard said...

Thanks for that point that out: I think the neater calling syntax does help.

I've also just spotted that the presentation is now available at:

http://developers.sun.com/learning/javaoneonline/2008/pdf/TS-5579.pdf

1:41 PM  
Blogger Chris W. Hansen said...

Good post.

The Int type in Scala does not have a getClass() method even though it does have methods like hashCode(), equals(Any) and toString(). One of the reasons is that it may get compiled down to a primitive. There are no primitives in Scala (or rather the use of primitives is an implementation detail for the compiler to figure out), but a value of this type may not have a class backing it.

The Scala class hierarchy starts with Any at the top, with two children, AnyRef and AnyVal. AnyRef is equivalent to java.lang.Object (and therefore has all of its methods - like getClass() in this case), and AnyVal is a parent class for things like Int, Double, etc. When you write a Scala class, you implicitly extend AnyRef.

See here:
ScalaDoc for Any

Hope that sheds some light on your compile errors.

The Scala folks are not opposed to adding a getClass() method to AnyVal types, but first somebody needs to determine a solution that makes sense and figure out the ramifications.

5:50 PM  
Blogger Richard said...

Thank you Chris - that does help.

Just for the record, when I remove the Integer declaration the error I was seeing was:

error: type mismatch;
found : Int
required: ?{val getClass: ?}
Note that implicit conversions are not applicable because they are ambiguous:
both method int2Integer in object Predef of type (Int)java.lang.Integer
and method intWrapper in object Predef of type (Int)scala.runtime.RichInt
are possible conversion functions from Int to ?{val getClass: ?}
println ("seven is of type "+seven.getClass())

...which I see is being thought about at https://lampsvn.epfl.ch/trac/scala/ticket/496

6:16 PM  

Post a Comment

<< Home