Scala Gets Operator Overloading Right

02/20/2010 Update: I took this blog post and turned it into a presentation to the Atlanta Scala User Group, which I gave in January 2010. The slides from that presentation are here.

05/31/2009 Update: As Mads Andersen pointed out, in the example below, I had the Complex multiplication wrong. As I was working through the example in the book, I typed it incorrectly. I could argue that this blog post was not about my mastery of complex numbers, and was actually meant to show how operator overloading works in Scala, but in the interest of all-around correctness, I have updated the code to contain the proper implementation of complex number multiplication, and I have worked out the examples by hand using the formula on the Wikipedia page to ensure their accuracy. Now, on to the original article.

I’ve been intrigued by Scala for a while now, and I’m finally taking the time to learn it. As I was reading in my book yesterday, I came to the section on operator overloading. Now, this is a topic that developers who’ve been exposed to it feel very strongly about. It’s not quite as rough a discussion as vi vs. Emacs, but it’s close. Some say that operator overloading makes for more elegant code. Some say it just confuses things. I’ve always been in the elegant camp. I think if you can provide operators for your own classes that work intuitively, you ought to be able to do it. In Java, think about how nice BigDecimal would be to use if you had + and – instead of add() and subtract(). Of course, as with anything of power, you have to be careful not to abuse it. It would make no sense to provide a + operator on a Date class, since adding two dates together makes no sense. You have to be smart about it, but having the ability to provide operators for your own classes performing intuitive functions is a good thing.

So as I’m reading the section on operator overloading, it was nice to see that even though you can override the standard mathematical operators, Scala still maintains the proper precedence that everyone is used to. By this I mean that a + b * c will execute the multiplication first, then add the product of b and c to a. The reason this is interesting is because another language that I still love, Smalltalk, does it wrong (or at least, completely differently), and not just for overloaded operators. Smalltalk has no precedence for mathematical operators at all, ever. So a + b * c will execute the + operation on a, passing in b, and then execute the * operation on that result, passing in c. Always. Thus, 2 + 3 * 5 = 25 in Smalltalk, even though it should equal 17. To get 17, you’d have to write the equation as 2 + (3 * 5). I always found that strange.

The canonical example for operator overloading is a class to represent Complex numbers. I will not break with tradition and will, in fact, steal borrow the one from the book. Here, then, is the definition of the Complex class

class Complex(val real:Int, val imaginary:Int) {
    def +(operand:Complex):Complex = {
        new Complex(real + operand.real, imaginary + operand.imaginary)
    }

    def *(operand:Complex):Complex = {
        new Complex(real * operand.real - imaginary * operand.imaginary,
            real * operand.imaginary + imaginary * operand.real)
    }

    override def toString() = {
        real + (if (imaginary < 0) "" else "+") + imaginary + "i"
    }
}

Notice that we have overridden both the + and * operators. They each take another instance of Complex as an argument, do the proper operations and return a new instance of Complex as their result. Just as you would expect. Now, to exercise these operators, we have this

val c1 = new Complex(1, 2)
val c2 = new Complex(2, -3)
val c3 = c1 + c2

val res = c1 + c2 * c3

printf("(%s) + (%s) * (%s) = %sn", c1, c2, c3, res)

val res1 = c1 + c2 * c3 + c1 * c2

printf("(%s) + (%s) * (%s) + (%s) * (%s) = %sn", c1, c2, c3, c1, c2, res1)

Lines 5 and 9 are the interesting parts. The result of running these statements looks like this

(1+2i) + (2-3i) * (3-1i) = 4-9i
(1+2i) + (2-3i) * (3-1i) + (1+2i) * (2-3i) = 12-8i

which is exactly what you’d expect.

Now, C++ people are probably saying, “But C++ has always done it right!” Indeed. But languages like Smalltalk and Scala are far more fun to work in.

Why I Love Closures

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