oh, technology

Menu Close

Closing the book on closures

I keep getting questions about closures, mostly from people who know me and my YAGNI attitude.  “I don’t get what’s so great about them.  What can you do with closures that you couldn’t do otherwise?”

Answer: Nothing.

Well, that’s only partly an answer.  It’s true, closures aren’t magic.  There’s nothing you can do with them that you couldn’t do otherwise, but there is something great about them.  For example, in Groovy, you could do something like this:

def sql = Sql.newInstance("jdbc:mysql://server/database", "sa", "pword", "Driver")
sql.eachRow("select foo, bar from foobars") { row ->
  def foo = row["foo"]
  def bar = row["bar"]
  print "Foo = $foo and bar = $bar"

Here, Groovy’s Sql class has a method called eachRow that accepts two parameters: a query, and a closure to execute for each row in the result.  Functionally, this is the same as:

// assume you've got your db connection setup
ResultSet rs = conn.execute("select foo, bar from foobars")
while rs.hasNext()
  def foo = rs.getString("foo")
  def bar = rs.getString("bar")
  print "Foo = $foo and bar = $bar"

…and so you may be forgiven for thinking closures are much ado about nothing.  After all, in both cases, all you’ve done is open a connection, execute a query, iterate over the results, and do something with each row.  Not a big deal, right?

Well, while functionally the two code samples are equivalent, the closure example reveals something special.  See the last two lines of the non-closure example, the calls to rs.close() and conn.close()?  You don’t need those when calling Sql.eachRow because it does it for you.  The designers of Groovy, in essence, recognized that when you’re dealing with databases it’s real common to open a connection, execute a query, iterate over the results, and do something with each row.  Oh yeah, and clean up the resources afterward.  Now, with repetitive things it’s a good idea to encapsulate the invariants (opening, iterating, and closing) and abstract what varies (the queries and what you do with the results).  The query’s already abstracted as a SQL string, so let’s abstract the code that processes the results and we’re good to go.

There’s something else.  What we’re doing when we pass a closure to the eachRow() method is metaprogramming — partially specifying, at runtime, the behavior of another object.  But we’re doing it responsibly; we’re not just clobbering or overriding some behavior, we’re fulfilling a contract.  Of course, without closures, you could accomplish the same thing by using, say, the command pattern.  (More or less this is how the Groovy runtime does it behind the scenes, anyway).  But the closure paradigm is cleaner and more convenient.

Which gets to the heart of it: using closures properly results in cleaner, more elegant code, and that’s all.  But isn’t that what you want, anyway?

© 2018 lysergicjava. All rights reserved.

Theme by Anders Norén.