Posted by Skrud at Wednesday, August 16th 2006 at 7:27pm
This is kind of an aside and update to my previous post…
I was walking around the office today and I noticed a printed sheet sticking out of a printer that caught my eye. It was a printed version of this page. Look familiar?
Even more of a coincidence, the author of that page is Elliot Rusty Harold who, in addition to writing entire books on the subject of Java IO made himself known to the Ruby community when he more or less sparked the infamous Monkey Knife Fight a while back about Minimal vs. Humane Interfaces, which is the last time I think I had a serious code-related discussion on my blog.
Tags: geek, java, programming, ruby | 2 comments
Posted by Skrud at Monday, August 14th 2006 at 8:43pm
Programming in Java is like a rollercoaster. At first I hated it. Then I started to like it a bit…. Then I hated it again. Then I discovered the robustness and completeness of Java’s Standard Library, and I started to like it again. Now I still appreciate the library, but I hate the language. The Java Programming language is probably one of the most verbose languages in existence. It takes a lot of effort just to express some of the simplest things.
I think this is because the design philosophy of Java is anal-retentive to the extreme. The library _is_ well-designed, but to the detriment of the programmer who has to deal with the textual overhead that all this flexibility demands. Take a simple example of a program that writes a text file to the screen. This is an extremely basic thing to do. Here is what it looks like in Java:
public class FileRead {
static void printFile(String filename) {
try {
BufferedReader reader = new BufferedReader(new InputStreamReader(
new FileInputStream(new File(filename))));
String line;
while ((line = reader.readLine()) != null) {
System.out.println(line);
}
} catch (FileNotFoundException e) {
System.err.printf("No such file found: %s\n", filename);
} catch (IOException e) {
System.err.println("Error reading from file.");
}
}
public static void main(String[] args) {
if (args.length == 1)
printFile(args[0]);
}
}
(I actually googled for a while hoping to find a shorter version of this thing, and I actually couldn’t. I spent a good ten minutes thinking “It can’t seriously be this long…”).
There are good reasons why this sort of program needs to be written this way. We want to read from a File, so we create a File, and a FileInputStream to read from it. The FileInputStream basically gives us a sequence of characters from the file, and implements an InputStream interface which all InputStream-type things share. This means that a FileInputStream can easily be interchanged with an InputStream from some other source – this is (in the “big picture”) a good thing, because it’s generic.
But I want to read from the file. So I need to convert that InputStream into something that can actually do reading, which is a Reader. An InputStreamReader converts an Input Stream to a Reader. But I want to read from my file one line at a time. That means I need to use a specific kind of Reader that lets me read by the line. That’s BufferedReader. And that’s why we have that really long line of code that’s creating a file, an input stream, and input stream reader and a buffered reader.
Also notice that we actually are forced to deal with the FileNotFoundException and the IOException errors. So in case something goes wrong with the file and reading process, we are forced to handle the error. In a lot of cases, this is a good thing – it means that the compiler won’t let you get away with some potential bugs. But what if we explicitly checked that the file existed before trying to read from it? What if we did:
File file = new File(filename);
if ( !file.exists() ) // ...
Well, we’d still have to handle the FileNotFoundException even if it would never, ever, ever occur because we’re checking for the file’s existence. That’s making things kind of messy. Plus – what if it’s a perfectly normal thing to have a file not exist? By definition, it wouldn’t be an Exception, but we have to handle the error anyway because Java has decided that we have to deal with FileNotFoundException no matter where it may be.
All this just means I have to write a lot more code than I should.
To contrast, here’s a Ruby program that does the exact same thing:
filename = ARGV[0]
open(filename).each_line { |line| print line }
Wow. It’s basically one line. And it even reads like English “Open filename, each line print line”. You don’t even have to be a programmer to figure out what that does. Mind you, what if “filename” doesn’t exist? You’re right, I’m not checking for any of the errors here. If the file doesn’t exist, the program will just exit with an error. But I guess just to push things closer to the Java version, I’ll add some error-handling.
filename = ARGV[0] or raise ArgumentError.new("No filename provided")
begin
open(filename).each_line { |line|
print line
}
rescue Errno::ENOENT
$stderr.puts("No such file: #{filename}")
end
I think that’s still pretty clean, even though the ENOENT might throw you off if you didn’t know it meant “No Entry”.
What about Python?
from sys import argv
if len( argv ) == 2:
try:
file = open( argv[1] )
for line in file.readlines():
print line,
except IOError:
print "Error reading from the file."
else:
print "No file specified."
Hey! That’s also really short. It just calls “open” and then loops over all the lines in the file. It doesn’t take a Computer Scientist to figure out what “file.readlines()” ought to do.
My point is that while Java is great at maintaining a generic, flexible design, it’s not very good at revealing the intentions of the programmer. I don’t think that code in Java is very readable, because you need to understand all the inner workings of the Java Library in order to understand what a simple piece of code that prints a file to the screen does. And I don’t think that should be necessary. I think you should be able to look at a piece of code and see “Oh, ‘print each line of the file’”. Any programming language that is as close as possible to “print each line from file” is certainly revealing the intentions of the programmer, and therefore is much easier to read and follow.
Java is great at revealing the internal workings of the library – waitaminute, isn’t that in contradiction to good Object Oriented Design? Aren’t you supposed to encapsulate and hide away your implementation details so that nobody needs to know how they work? Why then do I need to know that a File needs to be read through an InputStream and converted to a Reader and finally read? Why do I need to know that BufferedReader and LineNumberReader are the only Readers that let me read one line at a time?
WHY CAN’T I JUST SAY “FROM FILE PRINT EACH LINE”?
Alan Kay has a very famous quote:
Simple things should be simple and complex things should be possible.
Java makes complicated things possible, and simple things complicated. There is a huge variance between the code you write and the thing you want to be doing. I think the closer those are, the better.
Tags: geek, java, programming, python, rants, ruby | 17 comments
Posted by Skrud at Thursday, March 30th 2006 at 9:26am
Over at Stevey’s Blog Rants, there’s a great article entitled Execution in the Kingdom of Nouns. Stevey eloquently describes the problems inherent in the Java programming language’s emphasis on Object-Oriented Programming to the EXTREEEEME. There’s a lot of truth is the stories he tells of the problems in Javaland, and insightful comparisons to other Programming Language Kingdoms.
Java itself does force you to place things in objects, when maybe, an “object” is not always the best place for that thing. And while a Java program clearly reveals it’s architecture, it doesn’t do a very good job of revealing it’s intentions. I for one prefer intention-revealing code to anything else. I like being able to glance at a code fragment and say “I know what this does” – without having to look up a class reference API document.
Given all the Java programming I’ve been doing in one of our projects this semester, Stevey’s rant sure made me feel a bit better about being bitter. :)
Tags: code, geek, java, oop | 2 comments