I’ve been a big fan of closures for years. I was first introduced to them in Smalltalk, where they were just called blocks. Ruby has them and also calls them blocks. Java does not have them, though there are proposals (such as this one) to add them to a future version of the language. Groovy has them now, and while they aren’t as shiny as those in Ruby, they do work.
So what’s so great about closures anyway? They are blocks of code that retain links to things that were in scope when they were created, but they also have access to things that are in scope when they execute. They can be passed around, usually to methods that will execute them at a later time, in a possibly different context. That doesn’t sound all that exciting, but what is exciting is when the language in question lets you pass in a closure to do some work, and then cleans up after you when the work is done. Here’s an example of some Ruby code that I have written in tons of scripts.
count = 0 DBI.connect(*CREDENTIALS) do |con| File.open("results.txt", "w") do |file| con.select_all(sql) do |row| file.puts "... interesting data from the query ..." count += 1 end end end puts "Total records: #{count}"
What that code does is declare a variable, count, that will be used to keep track of how many records we processed. It then connects to a database, creates a file called “results.txt,” executes a SQL select, writes some of the data from each row to the file and increments the count variable. At the end, we print out the count variable.
There are three closures in that bit of code. They begin on lines 3, 4 and 5, respectively. The first connects to the database. The second creates the output file and the third performs a SQL select, writes the output and bumps the counter. Rather concise code, don’t you think?
Do you notice anything missing from this code? Two very important things are not there: closing the file and closing the database connection. I said these bits are not there, but they really are. The block version of DBI.connect ensures that no matter how events unfold, either successfully or with an exception, the database connection will get closed. Similarly, the File.open ensures the file will get closed. This is one of the most beautiful aspects of languages that support closures.
As I said earlier, Groovy has closure support baked in. While I’m not completely thrilled with the syntax, it’s close enough to Ruby’s that it’s not bad. As I was writing this post, I was surprised to discover that Groovy’s SQL module doesn’t support a closure passed to the connection, which means you still have to worry about closing your connection when you’re done. Anyway, Groovy’s file-writing idiom looks like this
new FileWriter("results.txt").withWriter {writer -> writer.write("... interesting data ...") }
I don’t really like using the -> as the closure parameter delimiter; I’d rather use a | (pipe symbol) like Smalltalk and Ruby do. Regardless of syntax differences, that’s how you write the file, but what about the SQL stuff? Since we can’t use a closure, it’s a bit more involved.
con = Sql.newInstance(url, user, pass, driver) try { new FileWriter("output.txt").withWriter {writer -> con.eachRow("select * from foo") {row -> writer.write("Foo: ${row.id}n") } } } finally { con.close() }
You can see that even though we can’t use a closure to ensure the database connection gets closed, there are still two closures in use. The one beginning on line five opens the file, while the one beginning on line six writes out some of the data from each row returned by the query. Pretty nifty, eh? I’m disappointed that Groovy doesn’t support passing a closure to the database connection, but maybe we’ll get that in a future version.
For comparison, here’s how you would write this program in straight Java.
Connection con = null; Statement stmt = null; ResultSet rs = null; try { Class.forName(driver); con = DriverManager.getConnection(url, user, pass); stmt = con.createStatement(); rs = stmt.executeQuery("select * from Foo"); PrintWriter writer = new PrintWriter(new FileWriter("output.txt")); try { while (rs.next()) { writer.println("Foo: " + rs.getString("Id")); } } finally { writer.close(); } } catch (Exception e) { e.printStackTrace(); } finally { try { if (rs != null) { rs.close(); } if (stmt != null) { stmt.close(); } if (con != null) { con.close(); } } catch (Exception e) { e.printStackTrace(); } }
The majority of that code deals with cleaning up when the real work has been finished. I don’t know what Java’s closures will ultimately look like, or if we’ll get them at all. I’m hopeful, though, that we’ll get a decent implementation, and can then jettison code like you see above. (I know that using Hibernate or some other mapping tool can eliminate code like this, but not every situation needs that type of framework.)
Don’t get me wrong. And I am not trying to defend java (and it stupid absence of nice features while we got other crappy ones instead) …but IMO your comparison is not really fair like that. It also depends on the APIs that are available how elegantly you can implement the above code. There are language and API features that need to go hand in hand. One could image java to have an API more like (attn: only email/comment coding!)
This isn’t nearly as elegant and anonymous classes aren’t really closures ….but it would also not be such a humongous piece of code than what you are using as java reference. See what I mean?
Thanks for the comment Torsten, but you illustrated my point. Sure, it’s possible to write Java code like you did, assuming someone else has already written ClosureWriter, RowClosure, etc. My point was that Ruby and Groovy allow you to do cool things, because of what’s in the language and its library, out of the box.
Uhh, the Groovy version looks really ugly. Doesn’t inspire me to try out Groovy.
Yes, Java can be very noisy, but pleeeeassssssssssseee… how much time did you spend on trying to maximize the verboseness of the java version?
As long as Ruby/Groovy/etc languages dont give me static compile time type checking with reasonable type inference and C#-like properties, there is no reason to switch to such languages. Scala comes close, but if feels too much different and goes to a little too far. Longtime maintainability is a nightmare without compile time static analysis. And that nightmare goes bigger the more people work on the code.
It is so trendy these days to feel “agile” when you have no compile-step or when you can write 3 LOC instead of 15.
Torsten is correct; your example is misleading–your examples have more to do with library support. I’ve been using something similar to Torsten’s example for years to de-clutter (as much as is possible in Java, anyway) my mainline code.
IMO a better example of closures would be more directly related to their root nature–function evaluation with bound variables, and how that function maintains the value of those bound variables. Their use is now mainstream due in no small part to JavaScript, easily one of the most misunderstood languages ever.
RowClosure is called Jdbc Template in Spring and several years old.
And I guess RowClosure and ClosureWriter would be 20 lines of code together, hardly a lot for any decent project.
It’s more a state of mind and style of development than about languages (the noise in Java aside).
See Functional Java.
Peace
-stephan
Thanks for the comments guys.
Sakuraba, I didn’t spend any time trying to make the Java version more verbose. That was actually straight out of my head, compiled on the first go.
Stephan, yes, Spring has JDBCTemplate that let’s you do similar things, but you have to download and install Spring. That’s not a big deal, but in both the Ruby and Groovy examples, nothing beyond what comes standard with the language was needed to have a much cleaner version than the Java version. That’s what I was trying to get at. Just like what Torsten showed, you absolutely can write Java that looks like Ruby, but you have to have more software than what comes with the JDK, or you have to write those wrappers yourself.
Caligula, you’re right. That probably would have been a better example, but I didn’t think of it.
Hi Joey,
The eachRow method should handle resources for you in Groovy. It should only be a problem if you want to dive under the covers and manipulate the connection itself.
Can you change the example to include bind variables in the sql? I’d like to see how that looks using a closure.
Aka in Java the inner class can implement several methods such as …
void bindParameters(PreparedStatement stmt);
void onEachRow(ResultSet rset);
In my book it would be a more realistic example to use named or positioned bind parameters and I’m interested to see what that looks like as a closure.
Thanks, Rob.
Paul, I used the eachRow method in my example, but it doesn’t handle closing the connection, does it?
out of interest, I “converted” your ruby example to pseudo javascript … I thought the result was quite interesting:
Re: “… I used the eachRow method …”
Yes, just checked the source code and (at least in trunk) every variation of eachRow that I could find closes the resources. This may not be the case for very old versions of Groovy, not sure, but fisheye and svn have all of the history.
Cheers, Paul.
Paul, that actually seems like the wrong thing to do. What if I want to do several selects against the same connection? I’ve actually written a wrapper for the Sql object that does what I want. I haven’t fully tested it yet, but I was able to write the code in Groovy that looked almost just like the code in Ruby.
By all means, consider a wrapper object if you think your circumstances require it, but JDBC drivers are pretty optimised these days. On top of which if you are using a pool, you will likely have some very good connection management going on. Then at least in 1.6-beta-1 and above you can always do sql.cacheStatements = true to turn on prepared statement caching within the Sql object (which implies connection caching) or alternatively use the cacheConnection() method which takes a closure and uses the same connection for all sql method calls within the closure. Some of this might not be documented well yet but hopefully will be before 1.6 final.
Re: “… I used the eachRow method …”
In Groovy, eachRow closes the resources ONLY if the connection provider is a DataSource. When the groovy.sql.Sql instance is created using a Connection instead of a DataSource, closing the connection is up to the calling code.
Oops, just checked the code again. My bad – I don’t know what I was thinking the other day – I must have been suffering from more lack of sleep that normal that day. You and Gordópaz are correct. Resources are closed only if you are using DataSources even with connection or statement caching in place. Sorry about the confusion.
And of course, Perl has had closures since the beginning of Perl 5, which was back in the pre-Internet days (1995-ish). There’s even an entire book dedicated to Perl’s closures… MJD’s “Higher Order Programming”. Perl: those who do not study it are doomed to reinvent it, badly.
“My point was that Ruby and Groovy allow you to do cool things, because of what’s in the language and its library, out of the box.”
That is not really an argument for closures though is it.
Anthony, I see your point. I probably didn’t make the case as clearly as I wanted. That comment was in response to someone who suggested using some features from a 3rd-party library. I was trying to say that the fact that the languages support closures allows you to do this sort of thing, without needing another library.
Hi,
I wonder what closure delivers when an error happened. In that Java example if something wrong happened when I am closing the connection, I know where it happened. Java will be able to report exact line number, because at minimum one statement corresponds to the particular action. With closure we have more actions than statements. Closure hides this clear correspondence from me by adding addition layer.
IMHO, it is better to have more lines of transparent code than fewer lines of obscure code.
Jacob, you’re still going to know exactly where the problem occurred. Any exception that is thrown will still have line numbers and anything else you’d expect to find. There’s no obfuscation of problem location.