Imagine someone took the training wheels off of Objective-C. That’s how I currently feel.
I’ve actually had a long—erm, not quite “love-hate”, more “‘sup?-meh”—relationship with Ruby. I’ve long wanted to tinker but never really had a project where I could make it fit; I did learn a little about Rails a couple of years back but didn’t then get to put it into practice. Recently I’ve been able to do some Real Work™ with Ruby, and wanted to share the experience.
Bear in mind that when I say I’ve been working with Ruby, I mean that I’ve been writing Objective-C in Ruby. This becomes clear when we see one of the problems I’ve been facing: I couldn’t work out how to indicate that a variable exposes some interface, until I realised I didn’t need to. Ruby takes the idea of duck typing much further than Objective-C does: using Ruby is much more like Smalltalk in that you don’t care what an object is, you care what it does. Currently no tools really support that way of working (and so Stockholm Syndrome-wielding developers will tell you that you don’t need such tools; just vi and a set of tests); the first warning I get when I’ve made a mistake is usually an exception backtrace. Something I had to learn quite quickly is that Ruby and Objective-C have different ideas of nil: Ruby behaves as the gods intend and lets you put nil into collections; but Objective-C behaves as the gods intend and lets you treat nil as a null object.
The problems I’ve been facing have largely involved learning how things are conventionally done. One example is that a library I was using took a particular parameter and treated it as a constant. Apparently Matz is a big fan of Fortran, but only early hipster Fortran before they sold out and added implicit none (around the time they fired their bass player and started playing the bigger venues). So Ruby provides its own implicit convention: constants have to be named starting with an uppercase. Otherwise you get told this:
wrong constant name parameter-value
Erm, that’s it. Not “you should try calling it ParameterValue“, or “constants must start with a capital letter”. Not even “this is not a good name for a constant”; who else interpreted that as “you gave the name of the wrong constant”? I think I’ve been spoiled by the improvements to the clang diagnostics over the last couple of years, but I found some of Ruby’s messages confusing and unhelpful. This is often the case with software that relies on convention: once you know the conventions you can go really fast, but when you don’t know them you feel like you’re being ignored or that it’s being obtuse.[*]
[*] When I asked for help on this issue I was told
I suggest you pick of[sic] a good Ruby book or watch some Ruby tutorials on YouTube; you’ll be pleased to know that the interpreter wasn’t the only ignorant or obtuse tool I had to deal with.
These are very neophyte problems though, and once I got past them I found that I was able to make good progress with the language. I was using LightTable and RubyMine for editing, and found that I could work really quickly with a combination of those editors and irb. Having an interactive environment or a REPL is amazing for trying out little things, particularly when you’re new at a language and don’t know what’s going to work. It’s a bit cumbersome for more involved tests, but the general execute-test cycle is much faster than with Objective-C.
Speaking of tests, I know that if you ask four Ruby developers how to write unit tests you’ll get six different answers and at least eighteen of them will have moved on to Node.JS. I’ve been using Mini::Test, as it’s part of the standard library so involved the least configuration to get going.
I also took the opportunity to install MacRuby and have a go at building a Mac app, using Cocoa Bindings on the UI side to work with controllers and models that I’d written in Ruby. This isn’t the first exposure I’ve had to a bridged environment: I’ve done a lot of Perl-Cocoa with CamelBones, the PerlObjCBridge and ObjectiveFramework. MacRuby isn’t like those bridges though, in that (as I understand it) MacRuby builds Ruby’s object model on top of NSObject and the Objective-C runtime so Ruby objects actually are ObjC objects. It means there’s less manual gluing: e.g. in Perl you might do:
my $string = NSString->alloc->initWithCString_encoding_("Hello", NSUTF8StringEncoding);
In MacRuby that becomes:
string = "Hello"
That’s not to say there’s no boilerplate. I found that by-return references need the creation of a Pointer object on the Ruby side to house the pointer to the object reference, which looks like this:
error = Pointer.new(:object) saveResult = string.writeToFile path, atomically: false, encoding: NSUTF8StringEncoding, error: error
For a long time, I’ve thought that there would be mileage in suggesting programmers use a different language than Objective-C for building applications in Cocoa, relying on ObjC as the systems language. Ruby could be that thing. The object models are very similar, so there isn’t a great deal of mind-twisting going on in exposing Objective-C classes and objects in Ruby. There’s a lot less “stuff you do that shuts the compiler up”, though ObjC has seen a reduction in that itself of late it still relies on C and all of its idiosyncrasies. Whether it’s actually better for some developers, and if so for whom, would need study.
Summarising, Ruby feels a lot like Objective-C without the stabilisers. You can work with objects and methods in a very similar way. The fast turnaround afforded by having an interactive shell and no compile-link waiting means you can go very quickly. The fact that you don’t get the same up-front analysis and reporting of problems means you can easily drive into a wall at full tilt. But at least you did so while you were having fun.