There seems to be no substitute for hanging out in a relaxed atmosphere and batting around ideas. There should be a lot more of that during next week's Programming Summer Camp.
This one has been bugging a lot of us for years. The best anyone could come up with is, "Uh ... it costs a lot?" but that doesn't really tell you anything.
There is one thing that does appear to differentiate enterprise software from consumer/small business/off-the-shelf software, and that's customization. It seems that if you're talking about enterprise software, you can never buy something off the shelf that just works -- customization is always involved. In fact for a lot of enterprise software, you can pay heavily for the "basic package" that doesn't do anything, then you pay 3 or 4 times as much (or sometimes more) before it actually does something useful.
You can look at software as a spectrum starting from "no customization" (consumer) at one end to "does nothing without customization" (enterprise) at the other.
What do you think?
James has been trying to create some very straightforward teaching examples using JDBC, and keeps getting foiled by checked exceptions. He pointed me to Howard Lewis Ship's recent post The Tragedy of Checked Exceptions (in the comments, there's an indirect link to some of my years-ago writings on the subject). In particular, James was very frustrated by the all the hoops he had to jump through in order to do something that ought to be simple. Even in the finally block he's forced to put more try-catch clauses because closing the connection can also cause exceptions. Where does it end? To do something simple you're forced to jump through hoop after hoop.
Then we started talking about the Go programming language, which I've been fascinated with lately because Rob Pike et. al. have clearly asked a lot of very incisive and fundamental questions about language design. Basically, they've taken everything we've started to accept about languages and asked, "Why?" about each one. Learning this language (which I've only started) really makes you think and wonder.
My impression is that the Go team has decided not to make any assumptions and to evolve the language only when it is clear that a feature is necessary. They don't seem to worry about doing changes that break old code -- they created a rewriting tool so that if they make such changes it will rewrite the code for you. This frees them to make the language an ongoing experiment to discover what's really necessary rather than doing Big Upfront Design.
One of the most interesting decisions they made is to leave out exceptions altogether. You read that right -- they aren't just leaving out checked exceptions. They're leaving out all exceptions.
The alternative is very simple, and at first it almost seems C-like. Because Go incorporated tuples from the beginning, you can easily return two objects from a function call:
result, err := functionCall()
(The ':=' tells Go that result and err are being defined here and that their types should be inferred).
That's it: for each call you get back the result object and an error object. You can check the error right away (which is typical, because if something fails it's unlikely you'll want to blithely go on to the next step), or check it later if that works.
At first this seems primitive, a regression to ancient times. But so far I've found that the decisions in Go are very well considered, and worth pondering. Am I simply reacting because my brain is exception-addled? How would this affect James' problem?
During our coffee conversation, it occurred to me that I've always seen exception handling as kind of a parallel execution path. If you hit an exception, you jump out of the normal path into this parallel execution path, a kind of "bizarro world" where you are no longer doing the things that you wrote, and instead jumping around into catch and finally clauses. It's this alternate-execution-path world that causes the problems that James is complaining about!
James creates an object. Ideally, object creation does not cause potential exceptions, but if it does you have to catch those. You have to follow creation with a try-finally
to make sure cleanup happens (the Python team realized that cleanup is not really an exceptional condition, but a separate problem so they created a different language construct so as to stop conflating the two). Any call that causes an exception stops the normal execution path and jumps (via parallel bizarro-world) to the catch clause.
One of the fundamental assumptions about exceptions are that we somehow benefit by collecting all the error handling code at the end of the block rather than handling errors at the point they occur. In both cases we want to stop normal execution, but exception handling has an automatic mechanism that throws you out of the normal execution path, jumps you into bizarro-parallel-exception world, then pops you back out again in the right handler.
Jumping into bizarro world is what causes the problems for James, and it adds more work for all programmers: because you can't know when something is going to happen (you can slip into bizarro world at any moment), you have to add layers of try blocks to ensure that nothing slips through the cracks. You end up having to do extra programming in order to compensate for the exception mechanism (It feels something like the extra work you have to do to compensate for shared-memory concurrency).
The Go team made the bold move of questioning all this, and saying, "Let's try it without exceptions and see what happens." Yes, this means that you're typically going to handle errors where they occur rather than clumping them all at the end of a try block. But hmm, that means that code that is about one thing is localized -- maybe not so bad. It also means you might not be able to easily combine common error-handling code (unless you identified that common code and put it into a function, also not so bad). But it definitely means you don't have to worry about having more than one possible execution path and all that entails.
I'm going to be watching the Go experiment closely to see how it pans out; for me, "success" means code is easier to write and read (I hope to start writing some experimental Go code in the near future).