Just A Summary

Piers Cawley Practices Punditry

Class decomposition and a handy delegation pattern 3

Posted by Piers Cawley Wed, 25 Nov 2009 22:49:00 GMT

There’s something satisfying about reaching the point when you can’t decompose an object any further and all your methods are tiny and do one thing – it’s especially gratifying when you learn something new in the process. Sadly, it doesn’t happen as often as I’d like, there’s usually annoying bits and pieces where you have to placate the language in some fashion that breaks the flow of what you’re writing.

As I get a better handle on the way MooseX::Declare has changed Perl, I’m finding I have to do much less in the way of placation.

Here’s an example. For context, I’m writing a traffic shaping tool. The basic client interface needs to look something like:

$policy->current_weight # => a percentage between 0 and 100

Not much of an interface really. The requirements state that we should be able to specify weights with 15 minute granularity for every day of the week. Our problem becomes one of mapping from a time to a number between 0 and 7 (days) * 24 (hours) * 4 (quarters) - 1 and looking up the weight in an array.

Here’s my first cut:

use MooseX::Declare;
class WeightVector {
    use DateTime;
    
    has vector => (
        isa => 'Array[Int]',
        is => 'ro',
        required => 1,
    );
    
    method current_weight {
        my $now = DateTime->now;
        my $offset = ($now->wday_0 * 7 * 24 + $now->hours) * 4 + int($now->minutes / 15);
        return $self->vector->[$offset];
    }
}

Which is, I suppose, perfectly respectable. However, current_weight isn’t filling me with delight. First it finds the current time, then it converts the time into an offset, then it uses the offset to lookup the weight in the vector. Let’s introduce a method to find the weight at a specific time"1":#decomp-motivation, the relevant code becomes:

method current_weight {
    $self->weight_at(DateTime->now);
}

method weight_at (DateTime $time) {
    my $offset = ($now->wday_0 * 7 * 24 + $now->hours) * 4 + int($now->minutes / 15);
    return $self->vector->[$offset];
}

And again, we could rest here, but again, we’re doing two things. We’re converting from a time to an offset, then we’re looking up the value in the vector. Type conversions tend to happen again and again, so it’s good if we can specify them separately. We could write a time_to_offset helper method, but we’re in Mooseland now; there’s a better way. Let’s introduce a formal Moose type and define a coercion for it. Here’s the type definition stanza of the code. I’ve taken the opportunity to add types which do bounds checking for the vector as well, while I’m about it.2

use MooseX::Declare;
class WeightVector {
    use DateTime;
    use Moose::Autobox;
    use MooseX::Types -declare => [qw(SlotOffset VectorOfWeights PercentageInt)];
    use MooseX::Types::Moose qw(ArrayRef Int);
    
    use constant TOTAL_SLOTS = 7 * 24 * 4;
    
    BEGIN {
        subtype PercentageInt,
            as Int,
            where { 0 <= $_ && $_ <= 100 },
            message { "$_ does not is not an integeter between 0 and 100" };
    
        subtype VectorOfWeights,
            as ArrayRef[PercentageInt],
            where { $_->length == TOTAL_SLOTS }
            message { "Vector must have ".TOTAL_SLOTS." entries, not ".$_->length };
    
        subtype SlotOffset,
            as Int,
            where { 0 <= $_ && $_ < TOTAL_SLOTS };
    
        class_type 'DateTime';
    
        coerce SlotOffset,
            from 'DateTime',
            via { ($_->wday_0 * 7 * 24 + $_->hours) * 4 + int($_->minutes / 15) };
     
        # Let's allow clients not to care about using DateTime by allowing 
        # them to simply pass the results of calling 'time()' - It's not like it's
        # still 1970...
    
        coerce SlotOffset
            from subtype(as => Int, where { $_ > TOTAL_SLOTS }
            via { to_SlotOffset(DateTime->from_epoch(epoch => $_) };
    }
    
    has vector => (
        is => 'ro',
        isa => VectorOfWeights,
        required => 1,
    }
    
    ...

Now, if we were programming in plain old Moose, we could rewrite weight_at like so:

sub weight_at {
    my $self = shift;
    my $offset = to_SlotOffset(shift);
    $self->vector->[$offset]
}

Which would be pretty sweet, but we’re using MooseX::Declare; there’s an even better way:

method weight_at (SlotOffset $offset does coerce) {
    $self->vector->[$offset];
}

Sweet!

We could stop there, but I had an insight. What we’ve got here is basically a wrapper around a delegation to our vector, and Moose’s new native types feature let us express the delegation to the vector quite neatly, like so:

has vector => (
    isa => 'VectorOfWeights',
    is => 'ro',
    required => 1,
    traits => ['Array'],
    handles {
        weight_at => 'get',
    },
);

...

around weight_at (SlotOffset $offset does coerce) {
    $self->$orig($offset);
}

This could be overkill when vector is a simple ArrayRef as we have here, but the pattern of delegating declaratively in the attribute definition and then munging arguments in an around handler is applicable to more than just argument transformation. A typical delegation pattern involves having the delegating object passing itself in as an argument to the method delegated to. The nature of Moose’s handles declarations makes that impossible to do within the attribute declaration, but it’s easy to fix with an around helper:

around delegated_method (Any @args) {
    $self->$orig($self, @args);
}

(If you’re wrapping more than one method in this fashion, you should probably consider using a plain old Moose style around handler, which lets you wrap multiple methods with around @delegated_methods => sub {...}

So, at the end of all that, and after we’ve extracted our Type declarations into WeightVector::Types, we have:

use MooseX::Declare;
class WeightVector {
    use WeightVector::Types qw(VectorOfWeights PercentageInt SlotOffset);
    
    has vector => (
        isa => 'VectorOfWeights',
        is => 'ro',
        required => 1,
        traits => ['Array'],
        handles {
            weight_at => 'get',
        },
    );
    
    method current_weight {
        $self->weight_at(time());
    }
    
    around weight_at (SlotOffset $offset does coerce) {
        $self->$orig($offset);
    }
}

And we’ve pushed all knowledge of DateTime off onto our type declarations and gained a boatload of handy bounds checking. We’ve also got a new tool for handling tricky delegation setups in the handles/around combo.

Notes

Motivation

I realise that this looks like a radical decomposition of the class with very little motivation, but it was driven by tests and by some other requirements that I’ve removed from the body of the post. In particular, the type coercions were driven by the need to build particular vectors for testing, a key method being:

method set_weight (PercentageInt $weight, 
                   SlotOffset $from does coerce,
                   SlotOffset $to does coerce)
{
    ...
}

Type coercion is wonderful

Generally, I’m not a fan of static typing. I’m from the “duck type all the way” school of programming,3 so most of my method declarations have no type declarations. But type declarations, especially ones that coerce, make so much sense on methods that make up the public protocol of a class. I only use type declarations on internal methods when I need a narrower coercion, or if I’m using MooseX::Multimethods, which I still haven’t used for anything but exploration.

Updates

Thanks to Chris Dolan for spotting that I’d got the SlotOffset coercion completely wrong. The real code’s doing the right thing, but that’s what comes of recreating code from memory.

1 This was actually motivated by trying to write tests to verify that the weights were correctly set.

2 I’m declaring these in a BEGIN block of the class itself mostly for explanatory purposes – there’s a good case for moving them out into a separate file and pulling it in with use.

3 Except during my periodic attempts to learn Haskell. I’ve learned Haskell at least three times now.

London.pm Presentation Video

Posted by Piers Cawley Wed, 13 May 2009 13:28:00 GMT

Back in (crikey) February, I gave a talk at the London Perl Mongers’ technical meeting about Moose for Ruby Programmers and wrote it up here. Mike Whittaker was in the front row of the audience with his iPhone and, a couple of minutes in, started a voice recording and gave me a copy.

So… finally… I’ve taken the time I should have been using to write another article for The H and wrestled the slides and the audio into something like sync and uploaded the results to Vimeo for your viewing pleasure.

An introduction to MooseX::Declare from Piers Cawley on Vimeo.

Test::Class::Sugar released 2

Posted by Piers Cawley Mon, 06 Apr 2009 10:15:00 GMT

I’ve just pushed the second version of Test::Class::Sugar (first discussed here). It’s pretty much as discussed in the original article, but after some discussion with David Wheeler, I’ve dropped the +uses clause from the testclass declaration in favour of less DWIMmy (and thus easier to explain behaviour).

I’ve also introduced a defaults argument at use time. The only working default in this release is one which lets you specify the prefix used to derive the test class name from the class/module under test. I’ve documented a couple of extra and so far unimplemented defaults as well.

Have a play, you might like it.

Writing parsers for fun and convenience 4

Posted by Piers Cawley Thu, 12 Mar 2009 03:29:00 GMT

One aspect of coming back to Perl for ‘recreational’ programming is that if, like me, you’ve declared war on @_ and boilerplate code, then testing can be somewhat trying. The Perl testing framework that best fits my head is Test::Class, which is an excellent Perlish implementation of xUnit style testing. If you’re unfamiliar with the, library, Ovid is writing what’s shaping up to be an excellent series of introductory articles about it at http://www.modernperlbooks.com/.

The problem I’m having with Test::Class at the moment is that I can’t write:

use MooseX::Declare
class Test::Person
  extends Test::Class
{
  use Test::Most;

  method class_under_test {'Person'}
  
  method startup : Test(startup => 1) {
    use_ok $test->class_under_test
  }

  ...
}

Test::Class is doing too much in its initialization phase, and relies too heavily on code attributes, for it to play well with MooseX::Declare. Drat.

On reflection though, this might be a good thing, because maybe MooseX::Declare isn’t really what’s needed. What I’d like to write is something like:

use ...;

testclass Test::Person
  exercises Person
{
  startup class under test should be usable (1 test) {
    use_ok $test->class_under_test
  }
}

And have the library ‘…’ expand the testclass declaration into something that looks like the first code snippet. After all, if MooseX::Declare can work without source filters, it should be possible to come up with something nicely declarative for specifying test classes.

Obviously, there’s nothing on CPAN that does this yet though. So I went fossicking through MooseX::Declare to see how it works1 and discovered thing of Lovecraftian beauty that is…

Devel::Declare

Devel::Declare is possibly the most hostilely documented library I’ve ever come across. Its documentation only begins to make sense when you already understand enough about how it works that you don’t really need the docs. What it does is to let you declare your own Perl keywords. You could, for instance use it to introduce given/when into versions of Perl that don’t have it yet. You declare your keywords and associate them with parsers. When, during its compilation phase, perl hits one of your keywords in the right context, it hands off to your parser which can then do what the hell it likes in the way of code transformation, before handing control back to Perl, which then parses the transformed code as if that was what was there all along.

So, to want to transform that testclass syntax I just pulled out of my ass into a real Test::Class package, I just need to write an appropriate parser and code generator, perform the appropriate Devel::Declare incantations, and I’m laughing.

Making progress

So far, I’ve got to the point where I have a working testclass keyword, but nothing yet for the ‘inner’ bits (setup, test, teardown, etc). I can write:

testclass Test::Person
  exercises Person
{
  ...
}

testclass Test::Person::Employee
  extends Test::Person
  exercises Person
{
  ...
}

and, as I write this, I’m realising that the syntax I’d cooked up for using extra test helper modules:

testclass AnotherTest
  helpers -More, -Exception, Carp
{
  # use Test::More;
  # use Test::Exception;
  # use Carp;

  ...
}

would probably read better as:

testclass AnotherTest
  +uses Carp
{
  # use Test::Most;
  # use Carp;

  ...
}

and also that I want this:

testclass exercises Person {
  ...
}

to build me a Test::Person class.

What’s still blowing my mind about Devel::Declare’s possibilities is that I’m no longer constrained to writing a Domain Specific Pidgin which works by building a tower of proxy objects and weird evaluation contexts to produce something that’s legal code in the host language, but which has the feel of another language. With Devel::Declare, I control the horizontal and the vertical until I choose to hand control back to Perl. Right now that means my error reporting is disgracefully bad, but it also means that I can roll a syntax that makes sense without worrying about how I’m going to get perl to parse it.

One of the things I find frustrating about writing RSpec specifications is that describe and it both want to be first class keywords – it feels like you should be able to write:

describe SomeClass, "in some context"
  before each
    # set things up
  end
 
  it "should do something or another"
    ...
  end
end

But, because RSpec works by taking advantage of Ruby’s block magic, you have to write:

describe SomeClass, "in some context" do
  before :each do 
    # set things up
  end

  it "should do something or another" do 
    ...
  end
end

I definitely prefer the version without the extraneous dos and the gratuitious : before each in the before declaration. Does anyone feel like writing devel/declare.rb?

Show us the code!

If you want to see the current state of my Test::Class::Sugar art, the place to look is http://www.github.com/pdcawley/test-class-sugar. At the time of writing it relies on http://www.github.com/rafl/devel-declare and doesn’t have anything so useful as documentation, a Makefile.PL or even any tests beyond the collection of code samples that is t/initial.t. Expect all those when and if I push it to CPAN.

Caveats

Yes, I know that this sort of metasyntactic abstraction is trivial in a Lisp. I just happen to like syntax, okay?

Update 20090312

use Test::Class::Sugar

testclass exercises Foo 
    +uses -Warn
{
    ...
}

Now generates

{
  package Test::Foo;
  use base qw/Test::Class/;
  use Test::Most;
  use Test::Warn

  ...
}

So that’s one hurdle jumped. And I now know how to write the various method helpers and, when I get the appropriately shaped tuits, I shall actually write the damned things.

Then all I have to do is document it.

And write up a proposal about it for YAPC::Europe.

Update 20090314

I now know what a plan looks like:

test with multiple assertions &lt;&lt; 3 {...}

And, more importantly, I’ve implemented, and documented everything and am almost good to cut a 0.001 distribution. I need a few ducks up on CPAN, but once that’s done, we’re good and I can get on with parameterizing some of the assumptions that are hard coded at the moment.

1 Something I swore blind I wasn’t going to do in my London.pm presentation. Seems my word isn’t to be trusted…

Marking time... 1

Posted by Piers Cawley Sat, 21 Feb 2009 00:41:00 GMT

So, it turns out that there’s a recording of my MooseX::Declare talk at the London.pm techmeet last night. And, on listening to it, I sound rather less incoherent than I thought I did while I was delivering it (still plenty of places where I could improve. Must remember to always repeat the questions in full…), but I’m happy enough with it that I’m looking into synching it up with the slides and making a slidecast, which will probable take longer than is sensible.

So, while I do that (and write it up properly as well), I thought I’d cover something I mentioned as a throwaway about using MooseX::Declare to easily set up what I called nonce classes for testing. A nonce class, in this context, is a one shot class that you can use to test inheritance, or to provide just enough behaviour to test the class you’re actually testing or whatever.

In ruby, it’s pretty simple really:

class Child1 < Announcements::Announcement
  attr_accessor :message
end

class Child2 < Announcements::Announcement; end

class GrandKid < Child1; end

Because ruby was designed as ‘Perl with better OO’, it has a convenient, compact notation for making classes, which means it’s easy and quick to define new classes, as you need them, near to where you’re going to use them. Which is very helpful in tests.

Meanwhile, in Perl, its a wee bit more… clunky. Assuming we’re using Moose, we have:

package Child1;
use Moose;
extends 'Announcements::Announcement';

has message => (is => 'rw', isa => 'str');
no Moose;

package Child2;
use Moose;
extends 'Announcements::Announcement';
no Moose;

package GrandKid
use Moose;
extends 'Child1';
no Moose;

Yikes.

So, let’s sprinkle some MooseX::Declare magic dust over things:

use MooseX::Declare
class Child1 extends Announcements::Announcement {
    has message => (is => 'rw', isa => 'str');
}

class Child2 extends Announcements::Announcement {
}

class GrandKid extends Child1 {
}

So, even if you completely eschew the really sexy bits of MooseX::Declare and simply use its new syntax for declaring classes, you’ve still improved your golf score and reduced your hoop count.



Just A Summary