Wednesday, May 30, 2007

Closures in Java: Why Bother?

Yes, I know Closures are Cool. All the "cool", "new" dynamic languages, like Ruby (which is actually older than Java, BTW), have them, so if Java is going to be "cool", Java needs closures.

Or does it?

My original title for this entry was "Closures in Java: Lipstick on a Pig?", but I decided against that because I don't want to imply any disdain for Java. I've programmed professionally in everything from IBM mainframe assembler language to (shudder) COBOL to Java to Ruby. I spent most of last year developing websites in Ruby on Rails. I currently develop client and server-side solutions in Java.

Professionally, I can't afford to be a language - or even a language-feature - snob. There are things I used to be able to do in assembler language that I can't do in Java, or even (gasp) Ruby. Do you see me writing proposals to add BALR functionality to the Java language? Of course not. Java is not IBM mainframe assembler language. It was never intended to be.

Nor was it intended to be Ruby. Or Python. Or ML. Or LISP. Or whatever your particular favorite non-Java programming language happens to be.

I wish the Java Powers That Be would leave well enough alone, be happy with the success of Java and the JVM, and stop trying to be Everything To Everybody.

Enough with the inferiority complex, already.

So Java doesn't have first-class functions (which some would say aren't essential for closures, but if you're not going to have first-class functions, why bother?). Big deal. It has a large install-base. It has Tomcat. It has the JVM. It has static typing, which is great if you're into that kind of thing. In short, it has everything you need to develop useful, useable, functional programs.

Could Java be a better language? Probably. But could it be worse? Certainly (yes, I'm talking to you, C++).

So why not just let Java be Java? If you really need first-class functions/blocks/closures, use Smalltalk. Or Ruby. Or Python. Or Lisp. If you really need first-class functions/blocks/closures on the JVM, use Smalltalk/JVM. Or JRuby. Or Jython. Or Armed Bear Common Lisp. Or any of the (astonishingly large) number of non-Java languages that have been implemented to run on the JVM.

Here's a thought: look at how JRuby or Jython implement blocks in Java code, and abstract that code into a new library for Java. Or use the existing Jakarta Commons Functor project. There are enough options available that hacking the language spec beyond recognition and bastardizing a decent language in the name of Keeping Up with the Language Jones-es should not have to be considered the only viable option.

Java is a Turing complete language -- by definition, you can do anything in Java that you can in any other Turing complete language. Which means that adding closures to Java, while an interesting theoretical and mental challenge, is not necessary.

So why bother?

Tuesday, May 29, 2007

Hacking JRuby

I recently got to contribute to the JRuby project -- issue #JRUBY-914: "JRuby's BigDecimal needs to round". In doing so, I got to learn a bit about Java's BigDecimal -- particularly how it differs from Java 1.4 to Java 5, implement a bit of code in the JRuby codebase, and test my ideas conveniently and interactively using Jython. Not a bad way to spend a couple hours hacking and contributing to the community.

In a nutshell, java.math.BigDecimal allows you to specify a scale, which is the number of digits to the right of the decimal place. So a BigDecimal with an unscaledValue of 1 and a scale of 1 would evaluate to 0.1. Java 5 allows the scale to take a negative value. So a BigDecimal with an unscaledValue of 1 and a scale of -1 would evaluate to 10. This is significant for rounding because it allows you to round a number to any digit, right or left of the decimal. But Java 1.4 does not allow a negative scale, so we need another approach to allow the same behavior on both versions.

My solution was to use the movePointLeft() and movePointRight() methods of the BigDecimal class to achieve the same result. It turns out those methods do permit a negative offset, so moving the decimal point "right" by a negative number actually moves it to the left, and vice versa. This turns out to be all we need to achieve the desired behavior: moving the decimal point to the right scale times positions the digit to be rounded to just to the left of the decimal point. We can then round normally, using whichever Java rounding mode suits our needs. Then we shift the decimal point left scale times to restore the (now rounded) number.

Note that this algorithm works for both negative and positive values of scale. It's a little bit of overkill when scale is non-negative, though, so I just implemented it for the interesting case of scale < 0.