General musings on programming languages, and Java.

Monday, May 28, 2007

Optional Checked Exceptions - Spitting at the compiler

In further anecdotal evidence that Gilad was right, Neal Gafter has blogged with a suggestion that checked exceptions could be removed from Java. He's right, they could. As far as I can see, all existing code would still compile, and what's more, it would actually work with existing bytecode with no problem, as the runtime has no problem with throwing undeclared checked exceptions. I've thought for a while that the problem with checked exceptions is that the API that throws them doesn't know whether you will care to deal with them.

First, I'll mention some ways in which closures can help with checked exceptions, then a mechanism for optional checked exceptions:

Reader.close() can throw an IOException, but we don't let it propagate. Closures to the rescue:
suppressing(IOException.class){ reader.close();}

Making Swing use the native look and feel requires catching about 5 exceptions, but we don't care about any of them:

loggingAndIgnoring(Exception.class){ UIManager.setLookAndFeel..}

Or even better:

whenExceptionsHappen({e => e.printStackTrace();}){ UIManager.setLookAndFeel...}

Suppose that we do want to propagate the exception instead, but we don't want to wrap it in a RuntimeException:

public void readsFromAFile() spits IOException

I'm not seriously suggesting 'spits' as a keyword, feel free to think of your own (I also like 'farts' and 'vomits', but that's because I've had a very busy weekend), but in this context, a method that spits IOException is declaring that it can throw an IOException, but that callers of it do not have to catch the IOException. That way everyone can be reasonably happy. API designers can still declare their exceptions using throws, guaranteeing that the client programmer has to think about the exception, but the client programmer can prevent the exception from leaking through their codebase, by using spits.

There is one problem - catching a checked exception from code that does not throw that exception is currently a compile-time error. Spat exceptions would make that compile-time error need to disappear too.

Challenge: think of a better keyword than 'spits' for this.

Thursday, May 24, 2007

You don't need TDD, you need a REPL

A walkthrough of how TDD can be badly applied to promote bugs, and an alternative. I'm imagining that my project is in Lisp, for the code samples, and so that I have a decent REPL (read-eval-print-loop, a place where you type code and see what it evaluates to). The situation described here is only partially based on reality, but you can judge how likely it is for yourself. 4:40pm Let's consider some code that we completely understand - adding two 2D points together. TDD says that I should write a test.. ok: (assert (equal (point+ (point 3 2) (point 4 6)) (point 7 8))) Then it says that I should make the test fail:

(defun point+ (first second)
  (point 1 2))
Ok, that fails. Now I should make it pass, and I should do the minimum required for that.
(defun point+ (first second)
  (point 7 8))
4:45 It passes. I get distracted by the telephone. 4:55 I look back, and I see that my test passes. Ok, I'm done, let's move on. None of my other tests use point+, because I try not to write dependent tests. So in this case, I will find the above bug in integration tests. But those involve files, and are a bit slower, so I don't bother running those quite so often. 5:00 My boss comes in and asks for a build for today's work, so that he can have a play around with it on his laptop on the train, which sets off in 10 minutes. I disable the integration tests temporarily so that I can just build it and give it to him, then dump a jar file to his USB pen drive. 5:10 He's on the train. 5:20 I'm distracted by the phone again. "All the drag and drop seems to have suddenly started going to the same place". Ok, I say, I'll fix it right now. So I re-enable all the integration tests, and yes, there's a failure. I fix it. 5:30 I email another build to my boss, but he's not able to get it until he's at home. While he was on the train, he gave a broken demo to a potential customer. You can find a few things wrong with my environment, such as that I don't use continuous integration, I actually answer phone calls while coding, and that I don't keep daily builds around for those times that my boss asks for a build. However, those are fairly superficial. The big problem that following TDD caused me was that I felt good about some really bad code. Without a test, I would have had suspicion for the code, but the test gave me confidence. That's not to say that testing is a bad thing, but that TDD gave me confidence in broken code. So let's implement the same thing again, but with a more 'distraction-proof' testing technique: 4:40 Write the function:
(defun point+ (first second)
  (point
    (+ (x first) (x second))
    (+ (y first) (y second))))
4:45 Check that it works for a few values in the REPL: (point+ (point 1 2) (point 3 4)) > 4 6 (point+ (point 1 4) (point 5 10)) > 6 14 ok, seems fine. 4:50 Get distracted. 4:55 Return to the code, there's no test yet, so I don't trust point+. Oh, but wait, look at the REPL, it looks like it works. I run the tests in the REPL again, then write a test case. (assert (equal (point+ (point 3 4) (point 5 6)) (point 8 10))) The test case passes, and this time I haven't made incomplete code pass tests. My boss, on the train, is happy, and so is the potential customer he demoed the software to. A REPL helps me to separate code that's part of my project from code that is just being thought about. It encourages experimentation, and hence thought, but doesn't get in the way should you get distracted and come back. It also makes you less likely to put broken code into the codebase, because it doesn't make you write experimental code in the production code.

Tuesday, May 22, 2007

I'm a Twit Too

After diligently avoiding finding out what twitter was, although it's hard to ignore it if you read technical blogs, two of my friends started using it and I joined them. It's amusing to see it go down every so often, and to think that Ruby is the culprit. Still, it goes down more gracefully than javalobby, and it's only lost one post of mine so far, which is a lot better than javaranch, and many blog sites. I'm going to keep mine technical, mainly on Java and Lisp at the moment, mainly as ideas for blog posts or projects that I'll probably never get around to. So if you'd like to steal my ideas, do whatever it is that twitter users do, or add the feed to your RSS reader. twitter ID - ricky_clarkson - http://twitter.com/ricky_clarkson

Thursday, May 17, 2007

Using just the JDK and 55 lines, I can find empty 'then' statements in Java code.

A few hours ago I noticed some comments on Joe Darcy's blog post about annotation processors.

I originally lost interest in annotations very quickly when I realised that annotation processors didn't have access to the full AST. I fiddled around with ANTLR a little, with CheckStyle, PMD and Findbugs' APIs to see what I could do. Not a lot, as it turned out. ANTLR looked promising, but as I recall, the language file for Java 5 was a bit flaky at the time. The others didn't provide me with complete types, so I couldn't detect things like code that tried to find a StringBuffer in a List of Strings. I gave up for a while, went off and learned to be productive in Haskell, and Lisp, and stuck my head into closures, which I'm very optimistic about.

I followed some links (about 6 deep) from Joe's blog, and came across the compiler API and compiler tree APIs. I have used it before, when I got some novice students to write their own compiling Java editor (that was fun), but I hadn't realised that you could get access to the actual compiler types - the standard API is rather thin. All you need to do is cast the task returned by the Java compiler, into a com.sun.source.util.JavacTask, and you're away.

I'm already mildly familiar with the compiler, having fiddled with its source a month or two back. I was trying to add a language feature that Stephen Colebourne, Stefan Schulz and I had been discussing, but we dropped the idea before I finished (well, before I really got started). Today I decided to attack a problem that used to catch me out when reading novices' code - an if statement with a semi-colon on the end, which just means 'if this, do nothing' and it executes the next statement regardless of the condition.

Javac uses the visitor pattern incredibly heavily, to a scale I've never seen anywhere else. I like it, but it caught me out temporarily. I don't really write subclasses anymore, I always use an alternative where practical. So I subclassed TreeScanner, which has nice 'return null;' implementations of every method, just ready for me to override. Which I didn't. Though I thought I was. The @Override annotation saved me when I realised..

Because I have my IDE set to Java version 5, and I wanted to run this code against version 6, I was just editing in vim and running from bash in Cygwin. No warnings about unused methods there then..

The short-term solution is to make sure I always remember @Override. The long-term, unlikely solution, is to persuade the javac project lead, sometime after Java 7, to accept my patch that makes these visitors more 'functional', so that instead of overriding, you give them a closure to invoke. The other advantage there is that you would be able to provide real booleans, rather than Booleans, etc.

There was only one more gotcha. Not only do I not subclass, I also don't use null very often, so I'd forgotten how to deal with boolean operators and null (I had to use Boolean, not boolean, thanks to generics). See if you can reduce my bestOf method into a chain of ternary operator calls. I couldn't do that without getting a NPE. I might investigate later.

There were no more gotchas. It was actually really easy, 30 minutes end to end. I included an 'empty' if statement in the source itself for testing. I think my next task should be to find places where @Override should be..

After all that fanfare, the code wouldn't fit in my blog's layout particularly well, so I'm afraid you'll have to click here to see it.

Blog Archive

About Me

A salsa dancing, DJing programmer from Manchester, England.