Feeds:
Posts
Comments

Posts Tagged ‘moose’

Even for a curmudgeon like me who doesn’t make much use of object orientation, Moose offers something which supports my programming with types style: a degree of run-time type checking and the ability to create a range of convenience functions with very little code.

Say I have a person who makes a bunch of orders consisting of a bunch of items. My hashref might look something like this:

my $data = {
    fred => {
        orders => [
            {
            order_id => 'fred1',
            items => [
                { description => 'roses' },
            ],
        },
            {
            order_id => 'fred1',
            items => [
                { description => 'one true ring' },
            ],

        },
        ],
    },
};

Then I’ll probably need a bunch of convenience functions to make sure I’m adding items to orders rather than people.

In Moose, including the type checking, that looks like:

Item

package Item;
use Moose;

has description => (
    is => 'ro',
    isa => 'Str',
);

no Moose;
__PACKAGE__->meta->make_immutable;

Order

package Order;
use Moose;

has order_id => (
    is => 'ro',
    isa => 'Str',
    required => 1,
);

has items => (
    is => 'rw',
    isa => 'ArrayRef[Item]',
    default => sub { [] },
    traits  => ['Array'],
    handles => {
        add_item  => 'push',
        get_items => 'elements',
    },
);

no Moose;
__PACKAGE__->meta->make_immutable;

Person

package Person;
use Moose;

has name => (
    is => 'ro',
    isa => 'Str',
    required => 1,
);

has orders =>
    is => 'rw',
    isa => 'ArrayRef[Order]',
    default => sub { [] },
    traits  => ['Array'],
    handles => {
        add_order  => 'push',
        get_orders => 'elements',
    },


no Moose;
__PACKAGE__->meta->make_immutable;

Adding an item as an order gives a nice error message:

my $fred = Person->new(name => 'fred');
my $item = Item->new(description => 'One true ring');
$fred->add_order($item);
$ perl moose-arrays.pl
A new member value for orders does not pass its type constraint because:
Validation failed for 'Order' with value Item=HASH(0x9ad3ec8)
(not isa Order) at moose-arrays.pl line 63

Moose ensures you pass an order to add_order(…).

my $order1 = Order->new(order_id => 'fred1');
my $item = Item->new(description => 'One true ring');
$order1->add_item($item);
$fred->add_order($order1);

my $order2 = Order->new(order_id => 'fred2');
$fred->add_order($order2);

use Data::Dumper;
print Dumper($fred);
$VAR1 = bless( {
                 'orders' => [
                               bless( {
                                        'order_id' => 'fred1',
                                        'items' => [
                                                     bless( {
                                                              'description' => 'One true ring'
                                                            }, 'Item' )
                                                   ]
                                      }, 'Order' ),
                               bless( {
                                        'order_id' => 'fred2',
                                        'items' => []
                                      }, 'Order' )
                             ],
                 'name' => 'fred'
               }, 'Person' );

I like it when someone else has already written the code I would otherwise need to write myself.

Read Full Post »

Moose Types

Rants about generalisations notwithstanding, I’m a fan of typeful programming (I’m sure I’d love Ada). For a script that will be moderately complex, I like to sit down and think about the types I’m going to use before I start.

Any library that will enable me to specify my types more precisely and concisely is obviously a win.

And speaking of Moose…

Moose has a bunch of methods to specify your types and a built-in type hierarchy available that you can build off.

Abusing an example I’ve used before:

use Moose;
use Moose::Util::TypeConstraints;
use MooseX::Params::Validate;

subtype 'LegalDrinkingAge'
    => as 'Int'
    => where { $_ >= 18 }
;

coerce 'LegalDrinkingAge'
    => from 'Int'
    => via { 1 }
;

sub can_legally_drink
{
    my $age = pos_validated_list(
        \@_,
        { isa => 'LegalDrinkingAge' },
    );

    return 1;
}

print can_legally_drink(18), "\n";
print can_legally_drink(17), "\n";

Checking for a LegalDrinkingAge type here is obviously the wrong thing to do, but for the purposes of the example it will do.

The resulting error is fine, if a little ugly.

$ perl5.10.1 moose-types.pl
1
Parameter #1 ("17") to main::can_legally_drink did not pass the 'checking type constraint for LegalDrinkingAge' callback
 at /u/packages/lib/perl5/site_perl/5.10.1/MooseX/Params/Validate.pm line 168
        MooseX::Params::Validate::pos_validated_list('ARRAY(0x976ce40)', 'HASH(0x911b6f8)') called at moose-types.pl line 24
        main::can_legally_drink(17) called at moose-types.pl line 33

Read Full Post »

Spurious Benchmark.pm Warnings

My most recent post has a few comments, but because I’ve been on holiday I didn’t reply to them in a timely manner. Sorry about that folks, I did read all of them.

And time has moved on so if I reply to those comments in the comments section, I’m guessing the original commenters won’t read the response. Having said that, they might not read this post either, but I think that is less likely.

Therefore this is a post to respond to the commenters, and to keep up with the IronMan schedule even though I’m not sure I’m being measured.

First of all, Roman, thanks for the comment. The figures you provide are interesting and yours is a second complaint about opaque error messages. That would bother me as well – I get enough of them from emacs macros and I have at least got tools (well, a tool – macroexpand) to debug those.

@Max – Hi. I’m not sure how you got the impression that I was interested in object creation time. We have primarily been talking about Moose start-up overhead here. I think I’m measuring the right thing.

@Anonymous – when you say "Run each benchmark item for 5 seconds or more." I think you missed this bit of the post:

Benchmark: timing 200 iterations of 1just-moose, 2all-includes, 3nocreate, 4create...
  1just-moose: 100.196 wallclock secs ( 0.05 usr +  0.25 sys =  0.30 CPU) @ 673.40/s (n=200)
2all-includes: 279.252 wallclock secs ( 0.08 usr +  0.34 sys =  0.42 CPU) @ 475.06/s (n=200)
    3nocreate: 318.256 wallclock secs ( 0.06 usr +  0.28 sys =  0.34 CPU) @ 581.40/s (n=200)
      4create: 320.979 wallclock secs ( 0.06 usr +  0.27 sys =  0.33 CPU) @ 611.62/s (n=200)

Not one of my benchmarks runs for less than a minute and a half. Most of them are around the five minute mark. And I have deadlines!

So where is this message coming from? I had a look in Benchmark.pm.

    print "            (warning: too few iterations for a reliable count)\n"
        if     $n < $Min_Count # 4
            || ($t->real < 1 && $n < 1000)
            || $t->cpu_a < $Min_CPU; # 0.4

My iterations is 200 so $Min_Count is not the problem. $t->real is at least 100 so that leaves $Min_CPU. Hmmm… yeah, I would have had to double the number of iterations to be sure. Look at 4create – 321 wallclock seconds of which 0.3 is CPU. No worries, I’m good – the warnings were spurious as I originally thought.

Read Full Post »

The debate over whether or not Moose is too slow rumbles on. On one side, are the folks using it in production (and presumably happpy with it). On the other, are folks saying it adds multiple seconds to the start-up time.

Following on from my benchmarking Moose startup overhead post, Sue D. Nymme provided code that actually created an object. Thanks Sue!

I took a look and Sue’s code uses MooseX::Declare that I haven’t installed. One magic CPAN invocation and, oh my gosh, more than 20 minutes later, MooseX::Declare is finally installed. Now I’m thinking that the benchmark time is actually measuring the time for perl to load in and parse MooseX::Declare and tens of thousands of lines of prerequisites rather than how slow Moose itself is.

I’ll compare just loading the modules Moose vs MooseX::Declare.

Then I’ll add the rest of Sue’s code.

And finally I’ll actually create a Moose object.

just-moose.pl

use Modern::Perl;

use Moose;
use Moose::Util::TypeConstraints;
use DateTime;

1;

all-includes.pl

use Modern::Perl;

use MooseX::Declare;
use Moose::Util::TypeConstraints;
use DateTime;

1;

And moose.pl (taken from Sue D. Nymme’s comment here.

bench.pl

use Benchmark qw(:hireswallclock);

use strict;
use warnings;

my $perl = 'c:/strawberry/perl/bin/perl';

Benchmark::timethese(200, {
    '1just-moose' =>
        sub { system(qq{$perl -e "require 'just-moose.pl';"}) },
    '2all-includes' =>
        sub { system(qq{$perl -e "require 'all-includes.pl';"}) },
    '3nocreate' => sub { system(qq{$perl -e "require 'moose.pl';"}) },
    '4create' =>
        sub { system(qq/$perl -e "require 'moose.pl'; MooseObject->new({ name => 'Bob' })"/) },
});

The Results

I’ve elided the too few iterations warnings.

Benchmark: timing 200 iterations of 1just-moose, 2all-includes, 3nocreate, 4create...
  1just-moose: 100.196 wallclock secs ( 0.05 usr +  0.25 sys =  0.30 CPU) @ 673.40/s (n=200)
2all-includes: 279.252 wallclock secs ( 0.08 usr +  0.34 sys =  0.42 CPU) @ 475.06/s (n=200)
    3nocreate: 318.256 wallclock secs ( 0.06 usr +  0.28 sys =  0.34 CPU) @ 581.40/s (n=200)
      4create: 320.979 wallclock secs ( 0.06 usr +  0.27 sys =  0.33 CPU) @ 611.62/s (n=200)

Conclusions

The bulk of the time is in loading the MooseX::Declare library. But on the other hand, declaring a single object did take a significant amount of time (approximately 40 seconds over 200 runs). I can now believe that declaring a lot of objects would be prohibitively expensive.

But that is using MooseX::Declare. I’m sure it is nowhere near as bad if we used Plain Ole Moose.

Read Full Post »

Sue D. Nymme comments on my blog:

I think Moose is fantastic, awesome, intuitive, helpful – except for one thing: its awful, unforgivable speed penalty. When writing GUI or CGI programs, I simply cannot wait eight to twelve seconds for a program to start. Ergo, none of my local library modules use Moose.

8 – 12 seconds for a program to start!? I don’t remember it being that quite bad when I used it.

moose.pl

use Moose;

1;

nomoose.pl :-)

1;

test.pl

use Benchmark qw(:hireswallclock);

use strict;
use warnings;

my $perl = 'c:/strawberry/perl/bin/perl';

Benchmark::timethese(100, {
    'moose'   => sub { system(qq{$perl moose.pl}) },
    'nomoose' => sub { system(qq{$perl nomoose.pl}) },
});
c:\home\jared> perl test.pl
Benchmark: timing 100 iterations of moose, nomoose...
     moose: 33.6734 wallclock secs ( 0.00 usr +  0.14 sys =  0.14 CPU) @ 709.22/s (n=100)
            (warning: too few iterations for a reliable count)
   nomoose: 1.25888 wallclock secs ( 0.06 usr +  0.17 sys =  0.23 CPU) @ 427.35/s (n=100)
            (warning: too few iterations for a reliable count)

Yikes, it’s more than 25 times slower! But is it really that bad? It’s adding between 0.3 and 0.35 seconds to the start time. That actually sounds quite reasonable. And when you consider that a typical large perl program will load a whole bunch of other modules which also add to the start time, maybe it will start to get lost in the noise.

Based on this test, rejecting Moose solely on start-up time overhead seems a little unreasonable. (There could be some more overhead if you actually try to use it mind you.)

test2.pl

Just in case I’d used Benchmark incorrectly, I tried something a bit more "traditional" (read hacky).

use strict;
use warnings;

my $script = shift;
my $perl = 'c:/strawberry/perl/bin/perl';

for (1..100) {
    system("$perl $script");
}
$ time perl test2.pl nomoose.pl

real    0m5.784s
user    0m0.015s
sys     0m0.000s

$ time perl test2.pl moose.pl

real    0m38.146s
user    0m0.000s
sys     0m0.046s

Read Full Post »

chromatic says that using Moose or a similar abstraction mechanism is an important indicator of Perl ability.

Pshaw.

I’m a 6th level Perlic User. I’m quite capable of constructing my own objects1 thanks very much.

This is chromatic:

Here’s a quick checklist to help those of you writing Perl to determine if you’re capable of writing Perl well:

  • Do you do this?
  • Do you do that?
  • Do you use Moose or another abstraction mechanism from the CPAN?

Okay you got me. I’m quoting out of context. Here’s chromatic again:

You don’t have to answer all of those questions in the correct way to write good and maintainable Perl, but if you answer most of those questions in the wrong way, of course you’ll write bad code.

I don’t know what these 18 items actually indicate, how well integrated you are with the perlective perhaps or how much you code like chromatic, but for sure only 6 or 7 at best are decent indicators of ability to write Perl well. I answered 9 of them in the wrong way and I’m certainly not rushing to correct the deficiency.


1. Having said that, I have used Moose before, and for the right project, I would use it again.

Read Full Post »

Follow

Get every new post delivered to your Inbox.