Learning Ruby for fun and profit

A month or so ago, it suddenly struck me that I am writing most of my programs in the same language (Perl) that I was using a decade ago. Sure, I’ve learned and used some other languages since then, notably Java and JavaScript, but neither of those has wired itself into my hindbrain the way C did in 1986 and Perl in 2000, so that they spring unbidden to my fingertips when I open a fresh emacs buffer to start a new program. Ten years is a long time to ride the same horse, especially when programming environments are moving so fast, so I decided to make a conscious effort to learn something new. Partly just to keep myself sharp; partly because learning a new language is learning a new way of thinking, which is useful when programming in an old language, too; and partly in the hope that I might find something that is just plain Better than Perl.

Don’t get me wrong – I like Perl. Obviously I do, or I wouldn’t have been using it as my Language Of Choice for a decade. I like it, but I can’t love it: it’s a language that was not so much designed as congealed, and consequently it encompasses a huge variety of ideas from a huge variety of sources, going back as far as COBOL. The various ideas don’t always sit comfortably on the same conceptual sofa, and there is a certain amount of pushing and shoving. All in all, it’s not a language that you would want to introduce to your mother. Still, these are easy flaws to forgive, because Perl is just so darned useful. I’ve hardly written C at all in those ten years, because Perl is capable enough (and fast enough) to let me do almost everything I used C for in the 1990s. (The last exception was a year or two back when I needed a program that would call setrlimit() to establish a desired environment, then execute a named program. I couldn’t do that in Perl because it seems to have no API to the setrlimit() system call, so I had a nice excuse to write some C.)

So I was looking for a new language to learn. Because I wanted something that was fun to work with, bondage-and-discipline languages with masses of compile-time checking were off the agenda; and because I wanted something with a sufficiently well established ecosystem that I could use it for Real Work right off the bat, novelties like Google’s Go were also not the way to go. For well established, widely used dynamic languages, the choice basically came down to Python or Ruby, which now have selections of packaged libraries on a par with Perl’s CPAN.

Both of those were appealing, and I nearly went with Python; but in the end it felt just a bit too familiar – a bit too much like cleaned-up Perl – whereas Ruby felt more like an adventure. So that’s what I went with, and a few weeks on it’s a decision I am really happy about.

How to learn a language? The obvious way these days, I guess, is from online tutorials and videos. But I am a bit old-school in these matters, and I’ve always found that the most effective way for me to learn a totally new technology is from a book – something I can take away from the screen, snuggle under a duvet near an open fire, with a glass of cheap port and a slice of mature cheddar at my side, and read without being continually tempted to Just Check My Mail. Luckily I have an old university friend (Steve Sykes) who is a big Ruby fan, so from him I was able to get a recommendation for what book to buy. Turns out that, just as The Book for C is Kernighan and Ritchie, so there is a The Book for Ruby: Programming Ruby: The Pragmatic Programmer’s Guide, by Dave Thomas. (I went with the version that describes the new 1.9 release of Ruby, which handles character sets correctly, rather than the older and more widespread 1.8).

While Ruby is an imperative object-oriented language, it also uses a lot of functional ideas – in fact Paul Graham has described it as “a dialect of Lisp with syntax”. One of the really neat things about it, which turns out to be trivial but has huge ramifications, is its syntax for closures (or blocks, as they are known in Ruby), which has finally won me over to how great closures are. I have to admit, they have always seemed to me like a fancy-dan computer-science-for-the-sake of it thing, and it doesn’t help that they are always described like this: “a closure is a first-class function with free variables that are bound in the lexical environment” (from Wikipedia). Yuck! No: I propose a new and better definition: “a closure is a callback that Just Works.”

Want to see? Here’s the Ruby secret sauce: if a method invocation is followed by a block of code (enclosed by {} or doend) then that block is passed as an anonymous closure to the method, which can invoke it using the yield keyword. That is all. Here’s how it looks:

 5.times { puts “hello” }
This does exactly what you’d expect: prints “hello” five times. It works this way. The integer 5, like everything else, is an object, and has methods. One of those methods is times, which if provided with a block invokes that block the specified number of times. In this case, the block just prints a constant message, but it needn’t. So here is one way to append the numbers from 10 to 15 to an array:
a = []
10.upto(15) { |val| a << val }
Within a block, the parameters passed to that block can be named by enclosing them in vertical bars, as we did with val above. Within the block, you can still see variables from outside the block – that’s what makes it a closure – which is why the access to a also works.

In a sense, there is nothing dramatically new about this: lots of languages have the ability to pass a closure into a method call and have it invoked as a callback. But it’s amazing what a difference syntax makes. Let’s see how that last example would look in Perl (assuming I’ve made a MyFixnum class which has the times method, since integers in Perl are just integers):

$ten = new MyFixnum(10);
@a = ();
$ten->upto(15, sub { ($val) = @_; push(@a, $val) });
It’s not just the dollar-signs and suchlike that make this cumbersome in Perl – it’s a general feeling of, well, that this isn’t how you’re expected to do it. It works, but it doesn’t feel happy about it. By making a relatively minor change to syntax – passing a following block as an anonymous closure that has easy access to named arguments – the Ruby version feels much neater and cleaner. It helps you to think in closurey terms, and that turns out to be a big win as we’ll see next time.

Of course, you can’t do this at all in languages like Java that just don’t support closures.

Finally, it’s worth comparing the Perl implementation of the MyFixnum class:

package MyFixnum;

sub new {
    $class = shift;
    ($val) = @<em>;
    return bless {
        val => $val,
    }, $class
}

sub upto {
    $this = shift;
    ($finish_at, $callback) = @</em>;

    for ($i = $this->{val}; $i <= $finish_at; $i++) {
        &$callback($i);
    }
}
with how we’d do the same thing in Ruby (rolling the loop by hand since I don’t want to use Fixnum.upto to implement MyFixnum.upto!):
class MyFixnum
  def initialize(val)
    @val = val
  end

  def upto(finish_at)
    i = @val
    while i <= finish_at
      yield i
      i += 1
    end
  end
end
Notice that the upto method doesn’t refer explicitly to the block at all – it’s implied by the use of the yield builtin. This small change makes the code much easier to read and nicer to work with. (It is a shame, though, that I have to use the hideous circumlocution i += 1, since Ruby for some bizarre reason doesn’t have a ++ operator.)

In the next article, I’ll compare how a Schwartzian Transform looks in Perl and Ruby (and maybe, if I can bring myself to write it, Java); and I’ll show you another trick or two using blocks.