Just A Summary

Piers Cawley Practices Punditry

Moose for Ruby programmers

‘Moose for Ruby Programmers’ was the programmed title for the talk I gave at the London.pm technical meet on Thursday, but that was something of a stalking horse for the real title, but I’ll save that for the end of this writeup.

Why Ruby?

I’ve said this before, and I’ll no doubt say it again, but the main reason I started programming in Ruby was that I got fed up of unrolling @_. From a Perl programmer’s perspective, Ruby is like Perl 5, but with a way better object system out of the box.

What’s so awful about unrolling @_?

Think about the way we program has changed over time. There’s been a trend of replacing conventions with code or, to look at it another way, of replacing imperative code with declarative code.

An example

Here’s some pseudo code explaining how we used to do code reuse back when everything got written in assembly language:

save stuff we need later
put the RETURN label on the stack
put arguments on the stack
goto FUNC_FOO
RETURN:
# return in accumulator
# saved values on the stack

Every time we called another bit of code, we had to jump through all these hoops in order to manage the resources of the machine. The particular sequence of hoops used within a program or library is called a calling convention. Nowadays we don’t have to do all that explicit control stuff, we just make a function call. Instead of a chunk of imperative code, we simply declare that we’re making a function call and let the computer do the bookkeeping.

On the other side of the function call, our function fulfils its side of the conventional bargain:

FUNC_FOO:
pop arguments
# maybe check types
# do the real work of the function,
put the result in the accumulator register
pop continuation
goto continuation

Of course, it’s much easier in a high level language like Perl:

sub somefunc {
my($argument) = @_;
$argument→isa(Whatever) or die;
# do stuff
return $result;
}

Hmm… actually… okay, so we’re not having to manage the call stack in Perl, but we’re still writing imperative code just to give names to our parameters. Meanwhile, in Ruby, we have:

def somefunc(argument)
argument.is_a?(Whatever) or raise
# do stuff
return result
end

One line shorter, and we get to name our parameters in a declarative style. We’re still stuck with imperative code to do the type checking, but we’ll live.

It seems such a silly reason to switch programming languages doesn’t it?

Wading in treacle

The trouble is, unrolling @_ is like wading through treacle. It exerts a great deal of drag. When every function you write carries with it the need to write a little bit of custom code just to do the simplest of tasks every time, it’s awfully tempting to just stick another level of indentation into your function and leave your worries about the Single Responsibility Principle to one side. I mean, what’s a 127 line method or two between friends?

And those little bits of custom code are great places for silly little bugs to hide. Most measures of defect rates seem to suggest that defects per line is pretty much constant across languages. There’s a very real benefit in writing concise, expressive code and that benefit isn’t simply the programmer’s idea of elegance. Concise languages “make the easy things easy, and the hard things possible”. If your programming style is heavily object oriented, Perl 5 doesn’t make the easy things as easy as they could be (and are in other languages). Ruby, for me, hit that sweet spot — it feels sufficiently perlish to make the transition easy, and its syntax for making classes and methods kicks Perl’s arse.

Of course, after a while, you’ll start hitting other limits and annoyances in Ruby (just as you will in any other language), it’s software after all and rule 1 of software is “Software sucks”.

The Moose effect

Although, in recent years, I’ve been pretty much exclusively a Ruby programmer, I’ve kept my eye on Perl and it’s been almost impossible not to notice the rise of Moose. To a Ruby programmer, I’m sure that the very idea of Moose would seem silly. Having to install an entirely new package and its dependency chain just to get something that looks a bit like Ruby’s class objects and accessor method auto generation? Are you kidding? Ruby’s stuff is good enough.

I used to think Perl’s OO was good enough.

It turns out that it is. Because if there’s one glorious thing about Perl it’s malleability. There’s very little you can’t change if you’re prepared to delve into the weirder bits of the way the runtime is put together. And, for all Perl has a reputation for oodles of syntax and weird special cases, once you get down to the bones, it’s consistent. The syntax to get there can be horrible, but the structure makes sense. So Perl has just enough object support for sufficiently clever programmers to build something really decent.

Here’s a bit of noddy code to show you what I mean. First, a ruby version and then I’ll show you the Moose equivalent.

class Article
attr_reader :title
def title=(a_string)
raise unless a_string.is_a?(String)
@title = a_string
end

attr_reader :body def body=(a_string) raise unless a_string.is_a?(String) @body = a_string end def initialize(initial_values = {}) self.title = initial_values[:title] end def print_on(html) html.h1(title) html.div(class => ‘body’) { body } end

end

And here’s the Perl equivalent.

package Article;
use Moose;

has title => (is => ‘rw’, isa => ‘Str’);
has body => (is => ‘rw’, isa => ‘Str’);

sub print_on {
my($self, $html) = @_;
$html→h1($self→title);
$html→div(
$self→body,
class => ‘body’
);
}

Note that the Ruby version omits the detailed error reporting of the Moose version, and if I were writing a real Ruby class, I wouldn’t bother with type checking something as simple as a string. It’s not really a fair comparison (I could tilt things far more steeply in Moose’s favour by using some of the more sophisticated attribute declaration possibilities that Moose offers).

The important thing to remember is that accessor methods are the most boring bits of writing a class. The interesting bits are the methods that do stuff. There’s a school of thought that says that accessor methods should be private (or at least protected) – otherwise you’re just using data structures with benefits. In any class worthy of the name, there will be more methods that do stuff than accessors after all, and I definitely prefer the Ruby version of print_on to the Moose one.

However, if we sprinkle a bit of magic dust on the Perl version, we have:

use MooseX::Declare;

class Article {
has title => (is => ‘rw’, isa => ‘Str’);
has body => (is => ‘rw’, isa => ‘Str’);

method print_on (Document $html) {
$html→h1($self→title);
$html→div(
$self→body,
class => ‘body’
);
}
}

I still find myself pinching myself when I see that. I can’t quite believe it’s still Perl 5 and I haven’t fallen through a wardrobe to a magical land where everything is flowers and ponies, and Perl 6 Christmas has arrived at last after too long a winter1.

But it is Perl 5 and it’s here now. Sort of (see Caveats below).

How does it work?

I neither know nor care2. As far as I’m concerned it’s sufficiently advanced rocket science and I’ll leave it at that. I’m just glad to be able to use it. About the only thing I can say with confidence about it is that Florian Ragwitz, Ash Berlin, Stevan Little and all the other who’ve made this possible are geniuses.

There’s more to it, of course, but the documentation covers things in
more detail than I’m happy to write here, so I’d suggest you read that if you want to know more. Generally, things just work the way you’d expect them to.

Caveats

This is seriously bleading edge code. There are bugs (though substantially fewer of them today than there were when I delivered the talk on Thursday), and you will trip over them. And when you do, the error reporting currently leaves something to be desired3, but it’s improving in leaps and bounds. The more people who are using it, the more people there are to submit patches (and a patch to the test suite with a bug and no fix is just as welcome as a patch that includes a fix).

So, if you’re starting a new project, you should consider going one step further than Moose, and using MooseX::Declare. Heck, if you’re feeling courageous, you should consider using it in existing projects as well. You can introduce it piecemeal after all – you can simply use class Foo {...} and only gradually introduce the extra method goodness as you refactor existing methods.

The real title

I knew I’d forgotten something. The real title of my talk was MooseX::Declare — Why I came back to Perl

1 I can’t quite believe I just used that metaphor.

2 Not quite true. It’s important that MooseX::Declare doesn’t use source filters, and it doesn’t, so that’s all right.

3 That’s the error reporting that happens when you trip a bug, not when you, for example, call a method that that expects an argument of one type with an argument of the wrong type, which is pretty exemplary.

Published on Sun, 22 Feb 2009 15:04:00 GMT by Piers Cawley under , , .

If you liked this article you can add me to Twitter
  • Gravatar

    By http://doodle.rubyforge.org/ Sun, 22 Feb 2009 18:52:39 GMT

    Hi Piers,

    If you like Moose in Perl, you might like Doodle for Ruby.

    For example:
    require ‘rubygems’
    require ‘doodle’

    class Article < Doodle has :title, :kind => String has :body, :kind => String def print_on(html) html.h1(title) html.div(body, :class => ‘body’) end end article = Article do title “Chaos and Law” body “The eternal struggle” end article # => # <Article:0×12cb9f0 @body=“The eternal struggle”, @title=“Chaos and Law”> a2 = Article do title 1 end
    1. ~> -:21: Article.title must be a kind of String – got Fixnum(1) (Doodle::ValidationError)

    Doodle also has validations, conversions, DSL helpers and more.

    Regards,
    Sean


  • Gravatar

    By CsabaU@Hotamail.com Mon, 23 Feb 2009 04:33:07 GMT

    Hi,

    Is also non-club members supposed to understand this?

    Regards
    Csaba


  • Gravatar

    By Piers Cawley Mon, 23 Feb 2009 05:56:58 GMT

    Well, it was primarily written for an audience of Perl programmers, and they certainly seemed to understand it when I delivered the talk.


  • Gravatar

    By draegtun Mon, 23 Feb 2009 12:56:34 GMT

    I think Piers is probably already aware of Doodle see doodling with moose

    Also note there is another Moose inspired Ruby module called classx on GitHub.

    Its good to see Moose having a good effect on Ruby as well and perhaps some useful cross pollination for the future?

    Piers – I really enjoyed your talk on Thurs.

    /I3az/ – Member of the Perl club!

    PS. Sorry for all my blog pimping links in the above ;-)


  • Gravatar

    By http://doodle.rubyforge.org/ Mon, 23 Feb 2009 21:33:16 GMT

    @draegtun Thanks for pointing out your post on doodle – I wasn’t aware of that before.

    FYI, Doodle wasn’t inspired by Moose at all – a case of convergent evolution :) But I’m definitely interested in it now.

    Regards,
    Sean


  • Gravatar

    By draegtun Tue, 24 Feb 2009 07:06:34 GMT

    @Sean – Also came across this yesterday which maybe of interest to u… Gazar / Aquarium

    This provides Ruby with the before, around & after modifiers ala Moose.

    /I3az/

    BTW (and note to Piers)… your doodle link in your first comment needs correcting.


  • Gravatar

    By Vidar Hokstad Sat, 28 Feb 2009 22:04:27 GMT

    Using the #is_a?’s in Ruby in the examples is really considered bad form and is certainly not idiomatic Ruby. If it quacks like a duck and all that – if a user passes an object that implements the required methods, then a Ruby programmer would generally expect it to work regardless of whether or not it was a String.


  • Gravatar

    By Piers Cawley Sun, 01 Mar 2009 03:46:28 GMT

    I’m well aware of the benefits of duck typing, and in general, I think code that tests on is_a? is just asking for trouble. However, please bear in mind that the code snippets shown were tailored for a slide presentation. Those param.is_a? String snippets were standing for the more complicated sanity checking that’s sometimes needed to make sure an object is valid. However, I’m sat in a hotel right now and I don’t have time to make a good example in this comment, so I’ll probably leave it for a later post.


  • Gravatar

    By Piers Harding Mon, 02 Mar 2009 16:15:13 GMT

    Hey Piers – this is truely amazing. I think a lot of us have been in the Perl-wilderness-years, but Moose* seems to be offering some hope of Christmas :-)

    Thanks for the write up.

    Cheers.


  • Gravatar

    By Piers Cawley Tue, 03 Mar 2009 03:59:49 GMT

    Isn’t it just? I was liking the look of Moose already, but MooseX::Declare takes it from ‘nice’ to ‘Whoah! I can do that?’. When you start looking at the modules underlying MooseX::Declare, it gets even nicer — MooseX::Declare isn’t a collection of egregious hacks, it’s a pretty straightforward application of some seriously powerful bits and bobs like Devel::Declare and B::Hooks::EndOfScope and, indirectly, Variable::Magic. It’s rocket science, certainly, but it’s multistage and each stage is reasonably comprehensible in its own context.


Comment Moose for Ruby programmers

Trackbacks are disabled

Powered by Publify – Thème Frédéric de Villamil | Photo Glenn