Adopting New JVM Languages in the Enterprise (Update) 106
(Updated to add Groovy, which I should have mentioned the first time. Also mentioned Django under Python.)
This is an exciting time to be a Java programmer. The pace of innovation for the Java language is slowing down, in part due to concerns that the language is growing too big and in part due to economic difficulties at Sun, which means there are fewer developers assigned to Java. However, the real crown jewel of the Java ecosystem, the JVM, has become an attractive platform for new languages. These languages give us exciting new opportunities for growth, while preserving our prior investment in code and deployment infrastructure.
This post emphasizes practical issues of evaluating and picking new JVM languages for an established Java-based enterprise.
The Big Redesign In The Sky 104
The first rule of holes: If you are in one, stop digging.
Many software developers take this to mean that if you have a huge legacy mess in your software you should stop working on it and rewrite it from the ground up.
This is probably the worst thing you could do…
Abstracting Away From Exceptions 55
I’ve been unsatisfied with exceptions for a long time. Sure, I use them and find them helpful, but I feel that they are a bit of a cop-out as a language construct. If an exception could talk, it would say “get me the hell out of here.” And, you have to admit, that’s a bit rude.
Exceptions seem to encourage “controlled abort” as an error handling policy. You throw an exception, it travels up the stack, and you catch it someplace else. All of your work is unwound (you hope) and then you are left with the task of logging the error or trying something else.
It is true – in many applications you do want to “fail fast” but not all applications are the same. Sometimes, you do want to go on, and it would be nice if exceptions had transactional semantics – it would be nice to have a guarantee that your program is in the same state it was before your try, in all of the ways that are important to you. Without that guarantee, programs devolve. “Log and fail” looks like the only option.
In this blog, I’m going to describe a first step toward rationalizing error handling in applications with pervasive exceptions. It’s a simple one, and now that I think about it, it probably doesn’t deserve all of the build up that I just gave it, but I suspect that I’ll be writing more about this topic. There’s definitely more to discuss.
A few days, I ago I tweeted that I was looking at condition systems for error handling. I was inspired by the fact that Ola Bini decided to add a condition system to his new language: Ioke. As far as I can tell, condition systems were first developed in Lisp. If I had to characterize the difference between an exception handling system and a condition system it would be that instead of saying “hey, get me the hell out of here”, a condition system says “I don’t know how to handle this, did you register something which could help?” All in all, it looks like a more reasonable approach.
After I tweeted this, Tracy Harms tweeted that the J Programming Language has a function called adverse which could be used as a the basis for a condition system. What does adverse do? Well as it turns out, it is very simple. Here’s a translation to C#:
public static class Adverse<T>
{
public delegate T Function();
public delegate T RecoveryFunction();
public static T Call(Function attemptFunction,
RecoveryFunction recoveryFunction)
{
try { return attemptFunction(); }
catch (Exception e) { return recoveryFunction(); }
}
}
Adverse accepts two functions. It calls the first and returns its value. However, if there is any kind of an error, it calls the second function and returns its value instead.
This might not look like a big deal. Superficially, it looks like another way to swallow exceptions. But, you can (of course) write an adverse which passes the caught exception to the second function. You can also write an adverse which works for void functions:
public static class Adverse
{
public delegate void Procedure();
public delegate void RecoveryProcedure(Exception e);
public static void Call(Procedure attempt,
RecoveryProcedure recovery)
{
try { attempt(); }
catch (Exception e) { recovery(e); }
}
}
How is adverse better than straight exception handling? Well, first of all, it forces us to separate responsibilities. And, in general, that’s good.
A long time ago, Bertrand Meyer described something called the Command/Query Separation Principle. The idea was that methods should either change something or return something back – they shouldn’t do both. The funny thing about exceptions is that they can do both and as a result we rarely think about structuring exceptions around the distinct actions of getting something or modifying something. And, that’s a shame. There are often cases where we are obliged to return something and it’s better that we return some sort of a Null Object rather than a null. In other cases, we need to focus our attention on what we need to do to patch up the state of our application. I suspect Adverse can help with that. There’s nothing that forces attention like the syntactic need to write some piece of code to use a construct.
I’ll end with some toy examples of Adverse in use (I haven’t put it through real paces yet):
string line = Adverse<string>.Call(GetLine, e => "");
string line2 = Adverse<string>.Call(GetLine,
e => { LogError(new ReadError(e));
return "";});
Adverse.Call(SomeWriteOperation,
e => LogError(new WriteError(e));
I’ve used lambdas here to be brief, but imagine plugging in your own handler function. You could start to parameterize your code with error handling policies. It could be interesting.
Refactoring Finds Dead Code 102
One of the many things that I just love about my job as a consultant/mentor is when I actually get to sit down with programmers and pair program with them. This doesn’t seem to happen nearly as often as I would like, so when two developers at a recent client site asked me if I could look at some legacy code to see if I could figure out how to get some tests around it, I jumped at the opportunity. We acquired a room equipped with a projector and a whiteboard. A laptop was connected to the projector and we were all able to comfortably view the code.
Fudge anyone? 86
Back in September, when I was just staring the Slim project, I made a crucial architectural decision. I made it dead wrong. And then life turned to fudge…
A Wish List for the Next Mainstream Programming Language 133
It’s been fun watching the reactions to new features in C# 4.0. Some people love them. Others wonder, legitimately, where it is all going to end. The argument for feature addition is simple: over time we find better ways of doing things and our languages, as tools, should allow us that flexibility. The only downside to this strategy is that you end up with sprawling, complex languages over time – you never get to revisit the foundations.
Fortunately, however, people design new languages all the time and some of them do eventually enter the mainstream. We get a chance to start over and address foundational problems. And, that’s nice because we can do better than Java and C# for mainstream development and I don’t think there is any way to mutate either language into a better foundation.
Before I launch into the wish list, however, I want to set the context.
When I say “mainstream language” I am talking about languages which are in the Java/C#/VB market space – languages which are light on rocket science, seen as suitable for large-scale development, and don’t scare people. So, I’m not going to suggest dynamic typing or (on the other side of the coin) tight Hindley-Milner type systems and lazy evaluation. I love those approaches and I’m happy (in particular) that Ruby is gaining widespread acceptance, but I’m not going to fight that fight here. In the immediate future, for whatever reason, there will be development shops which feel much more comfortable with traditional static typing – the kind found in Java, C#, and VB. Given that, the question becomes: what can we do to make that sort of language better?
Here’s my list:
- Immutability by Default – Over the past few years, a rather broad consensus has emerged around the idea that code is easier to understand and maintain when it has less mutable state. This isn’t a new idea; it’s been around for as long as functional programming, but our recent concerns with concurrency and our move toward multi-core computing just underscore the state problem. A mainstream language should, at the very least, make mutable data something special. References should be immutable by default and mutable state should be marked by a special keyword so that its use leaps out at you. It’s too late for the current crop of languages to make such a pervasive change, but the next mainstream language could.
- Support for Components – In large-scale development, teams have to manage usage and dependency across an organization. The notions of public, protected, and private are too coarse as protection mechanisms, and we really need scopes larger than class, assembly, and package. In the end, this sort of protection is a social issue, so we should have mechanisms which make use very easy within a team (3-10 people working together) and somewhat more manageable between teams. It’s odd that language-based support for this work stopped with Ada and the Modula family of languages. Java’s recent move toward support for modules seems to be an exception.
- Support for Scoped and Explicit Metaprogramming – In the past, language designers avoided adding meta-programming support to their languages because they were scared it would be abused. However, we’ve learned that without meta-programming support, people create yet another distinctive type of mess. If there is a middle ground for mainstream languages it probably involves scoping the use of metaprogramming and making it far more detectable. If, for instance, all of the code which modifies a given component had to be registered with some easily locatable component-specific construct, maintenance would be much easier.
- Support for Testing – This one is only a matter of time, I think. In the last 10 years we’ve seen an explosion of mocking tools and testing frameworks. It’s not clear to me that we’ve reached any sort of consensus yet, but I suspect that at the very least we could add constructs to a language which make mocking and stubbing much easier. It’s also about time that languages attempt to solve the problems that dependency injection addresses.
- Imposed I/O Separation – This is the controversial one. The more I work with Haskell, the more I notice that there is a beneficial discipline that comes from factoring your program so that its points of contact with the outside world can not be mixed with the pieces doing the work. When you first start to work that way, it feels like a straitjacket, but I think the benefit is apparent to anyone who has had to go on a spelunking expedition in an application to find and mock parts of the system which touch the outside world. Is that discipline too much for a mainstream language? I hope not.
So, that’s my list. There is no “grand language planning board” which decides these things. We will move forward chaotically like ever other industry, but I do hope that some of these features make it into the next mainstream programming language.
Glory and success are not a destination, they are a point of origin. 31
In this blog, Dean Wampler discusses what he learned about Craftsmanship from the master chef Rino Baglio. Remarkably, Chef Baglio responded to Dean’s blog and added the pregnant phrase that I chose for the title of this blog.: Glory and success are not a destination, they are a point of origin.
Hudson -- A Very Quick Demo 507
I recently set up Hudson as the continuous integration server for FitNesse. I was impressed at how very very simple this was to do. The Hudson folks have adopted the same philosophy that we adopted for FitNesse: download-and-go.
So I recorded a quick (<5min) video showing you how I set this up. In this video you'll see me start Hudson from scratch, create the job to build FitNesse, and then run the FitNesse build.
How to Guarantee That Your Software Will Suck 57
This blog is a quick comment about Justin Etheredge’s blog by the same name.
The promise of Shalt. 14
When writing requirements we often use the word “shall”. In Rspec, we use the word “should”. Some folks use the word “must”. These statements create the connotation of a law, or a command.
Let’s take a very old law: “Thou shalt not kill.” There are two ways to look at this law. We could look at it as a command[ment], or … we could look at it as a promise.
Here are the two different versions:
- Thou shalt not kill!
- Thou shalt not kill.
The difference is in the punctuation. And what a difference it makes. One is a stern order, a demand. The other is a promise… a confident guarantee… a statement of simple truth.
Now look at these rspec statements from RubySlim. I started writing the specs this way because it was shorter; but I like the connotation of confidence and truth. “I can has cheezburger.”
describe StatementExecutor do
before do
@caller = StatementExecutor.new
end
it "can create an instance" do
response = @caller.create("x", "TestModule::TestSlim",[])
response.should == "OK"
x = @caller.instance("x")
x.class.name.should == "TestModule::TestSlim"
end
it "can create an instance with arguments" do
response = @caller.create("x", "TestModule::TestSlimWithArguments", ["3"])
response.should == "OK"
x = @caller.instance("x")
x.arg.should == "3"
end
it "can't create an instance with the wrong number of arguments" do
result = @caller.create("x", "TestModule::TestSlim", ["noSuchArgument"])
result.should include(Statement::EXCEPTION_TAG + "message:<<COULD_NOT_INVOKE_CONSTRUCTOR TestModule::TestSlim[1]>>")
end
it "can't create an instance if there is no class" do
result = @caller.create("x", "TestModule::NoSuchClass", [])
result.should include(Statement::EXCEPTION_TAG + "message:<<COULD_NOT_INVOKE_CONSTRUCTOR TestModule::NoSuchClass failed to find in []>>")
end
end
This makes we want to change all the “should’ statement into “will” statement, or “does” statements.