I spent a fair bit of time over the last two months with Ruby. I actually started studying it about six months before, but I hadn’t really got to the point of writing anything non-trivial with it. At that point, it was more “theoretical”, than real. So two months ago I started doing some more complex stuff. For example, I wrote this little distributed abstract syntax tree interpreter. It’s nothing to brag about, and it’s probably completely useless, so I won’t bother explaining it, but the point was to understand Ruby a bit better.
As I expected, there were a number of areas where Ruby was beautifully brilliant and easy to work with. There were some disappointments however. That’s not really surprising either, since Ruby hasn’t been in the limelight all that long, and as a result many of the more expensive peripheral things like tools and libraries haven’t matured yet. And of course, like any language/platform, there are going to be room for improvement. There will be tradeoffs, things the community is working on, etc.
To be fair, I should probably start by listing all the great things about Ruby, but plenty of people who know a lot more about Ruby than me have already done that, so I doubt I’d really add much through that. Let’s just accept that I do believe there are great things.
That said I’m going to start taking potshots. Well, potshots is a bit harsh, but since I’m sure someone will feel like that’s what they are, we’ll just label them as such, and avoid all confusion.
IDEs
The first disappointment was IDEs. I started with Ruby in Steel, an add-in for Visual Studio .NET. The beginning was rocky as the first installers had some issues, but eventually I got it installed (and the newer installers are smoother). But I was immediately disappointed because I thought there was some rudimentary Intellisense support. There wasn’t however. The SapphireSteel guys are still working on that feature for their developer edition, and it sounds like it will be better than what I expected could be done, but we’ll have to wait and see. Oh, and the debugger was being troublesome.
The next up was RDT for Eclipse. Despite also advertising code completion, I found no signs of it. And once again the debugger was very troublesome.
I was about to give up on the idea of a debugger at all, when I found ArachnoRuby. It didn’t have or advertise Intellisense, but it did have a working debugger. Actually, it wasn’t just working, but designed for dynamic languages. For example, it allows you to attach a IRB console to a running program (after a breakpoint has occurred), and make modifications just like you had run your whole program in IRB.
Beyond that, another great thing was the Ruby Class Browser, which was far more useful than the standard documentation, or RDoc for finding classes and methods. Still however, there was no Intellisense, which I think would have been better because I got annoyed at having to swap windows to look something up.
So, it’s not all doom and gloom, but clearly, Ruby IDEs have a lot of room for improvement. And some features, like refactoring and Intellisense are much more difficult than they would be in a non-dynamic language. That’s one of those little tradeoffs. The smart dedicated Ruby people will probably just shrug their shoulders at those issues and move on about their work, accepting it for what it is, a tradeoff. The really brilliant and really dedicated Ruby people will find a way around 90% or 95% of the problem, although that will take time.
The idealists will probably respond that those features aren’t necessary, or are actually, despite common wisdom, are evil. Something along the lines of “Code Completion makes you stupid”, or “My Dual Core 3Ghz processor is too slow to run anything more complex than a text editor.” Oops, didn’t mean to start ranting like that… Moving on.
Unit Testing
Unit testing in Ruby is actually very good, despite the lack of any IDE support for it. Being dynamic is the biggest contributor to this. The array and hash primitives are also great for setting up test cases. But mostly it’s the dynamic nature of Ruby. Tests get virtually no benefits from static types.
Except the problem is, I found myself writing tests that would have been covered by a static type system. It’s not that the test cases would have been any easier to write with a static language, but there were plenty of them I wouldn’t have had to write at all. And even then, there were even more tests I didn’t write that would have been provided by default through a static language. And beyond this, in order to implement some tests, I had to put the equivalent of static typing into my code. It just ended up being a lot more verbose then simply declaring the type would have been.
For example, take this initializer:
def initialize(*exprList)
@exprList = exprList
@exprList = *@exprList if @exprList.is_a?(Array)
raise "Values must be interpretable" unless @exprList.all? {|value|
value.respond_to?(:interpret)}
end
.csharpcode, .csharpcode pre
{
font-size: small;
color: black;
font-family: consolas, “Courier New”, courier, monospace;
background-color: #ffffff;
/*white-space: pre;*/
}
.csharpcode pre { margin: 0em; }
.csharpcode .rem { color: #008000; }
.csharpcode .kwrd { color: #0000ff; }
.csharpcode .str { color: #006080; }
.csharpcode .op { color: #0000c0; }
.csharpcode .preproc { color: #cc6633; }
.csharpcode .asp { background-color: #ffff00; }
.csharpcode .html { color: #800000; }
.csharpcode .attr { color: #ff0000; }
.csharpcode .alt
{
background-color: #f4f4f4;
width: 100%;
margin: 0em;
}
.csharpcode .lnum { color: #606060; }
(I know I probably screwed up a dozen things, so it’s worthless to nitpick my code quality. I’m an admitted Ruby Nuby) That last line is really the crux of the issue here. I expect some will feel it’s simply not necessary, but the truth is, it helped solve more than one bug when I combined it with unit testing. But that line is really just a really verbose form of List<Interpretable>.
So what we have here, is just another tradeoff right? Dynamic languages make unit tests much easier to write, but require more of them. But what if we started to treat static type information as a compact, concise and expressive form of unit tests, rather than as a straitjacket. Maybe if we did this, by making static languages that support dynamic capabilities, we could make unit testing easy, and avoid rolling our own static typing. Just an idea.
Summary
There is more to all of this then simply potshots at Ruby. The real point, is that we all have a lot to learn from each other. Here I’ve shown how the Ruby tool developers are learning from the Java/.NET tool developers, though obviously they have their own unique challenges AND opportunities as well. But likely, all of the hard work put into Ruby Intellisense will have some real value to static language tool designers as well, especially those working with type inference.
I’ve also shown how static language unit test tool developers can learn from Ruby unit testing. I’m going to assume there is probably a way to make the Ruby side of things more palatable as well, though I don’t know what it is yet. When you get criticism, look at it as an opportunity to grow, not an attack, even if it actually is an attack (this wasn’t).
Recent Comments