[Templates] Meet the Badger (TT3's half-cousin) Redux

Andy Wardley abw at wardley.org
Tue Sep 9 13:59:03 BST 2008


Hi Stevan,

Thanks for taking the time to set me straight. I apologise for making such a
bad job of comparing the two. I can assure you that it was only my own
ignorance at fault and no malice was intended. I hope I didn't cause any
offense.

After writing that message I did a little more digging and realised that I was
wrong (or at best, way behind the times) on some issues. So I already made a
few corrections before posting it into the FAQ:

http://badgerpower.com/docs/FAQ.html#section_How_is_Badger_similar_to_different_from_Moose_

But it looks like I've still got some work to do...

> but *not* to be Perl 6 on Perl 5.

Yes, that's one of things where I already realised I was wrong.

> As for the clever magic, you will find very little of it inside  

Ah, but the emphasis was on *clever* rather than magic.  :-)

And there's some *really* clever stuff in Moose (and Class::MOP). It is magic,
in the sense that it happens behind the scenes, but it's definitely good
magic. Not to be confused with...

> Deep magic in Perl is almost always fragile

Agreed.   <shudder>

> Moose too uses the "regular" Perl 5 object model, don't be fooled by  
> the nice syntactic sugar, it is only sugar, underneath it all Moose  
> is just plain old vanilla Perl 5 hash-based OO.

Yes, totally understood.  I think what I was trying to get at was the
different syntactic style of creating object classes and how closely
that maps onto to the underlying old school Perl 5 implementation (or not).

For example, writing 'use Moose' will have an action at a distance by
automatically making your package a subclass of Moose::Object, importing
'has', 'extends', and so on. I'm not saying it's a bad thing. On the contrary,
I think it's an excellent thing. But it is a different way of working to the
old school Perl 5 way of explicitly defining subclasses with 'use base' and
importing symbols by name. Having lived under the repressive shadow
of Exporter's stern "Do not export anything without good reason" warning,
it's taking me a little time to adjust to this new way of thinking. :-)

(BTW I just found Moose::Unsweetened which is very useful here)

Badger's evolution has been much more closely aligned with the old school Perl
5 object model, as an accident of history rather than design (Badger is mostly
re-churned code from the last 10 years rather than a new design from scratch).
As a result, I think there's less of a mental leap than that required to leave
the old school Perl 5 rules behind and start thinking in terms of new school
Moose.

So down at the bottom of it all, you can ignore all the new-fangled stuff and
just write old school Perl.  So if you just want a good old-fashioned base
class and an accessor method or two, then the following is sufficient:

     use base qw( Badger::Base Class::Accessor );
     __PACKAGE__->mk_accessors('foo');

(Of course, there's no reason that I'm aware of why you can't just subclass
direct from Moose::Object and bypass Moose. I'm not sure what the implications
are, but it certainly seems to work, even if it's not advertised as such).

The declarative metaprogramming side of Badger is perhaps a little more
accessible to the average old school Perl 5 buff like me because there's a
closer mapping to the underlying concepts.

   use Badger::Class
       base      => 'Badger::Base',
       accessors => 'foo';

In this case, the 'base' keyword is similar to Moose's 'extends' keyword and
'accessors' is a very simple form of 'has'. The key difference is that
Badger::Class is *only* pushing 'Badger::Base' onto @ISA (just like base.pm)
and defining a foo() accessor method (just like Class::Accessor). The
important things is that the target object doesn't need to inherit from
Badger::Base, Badger::Class or have any extra keywords defined to make it
work. So you can use Badger to build classes that have no traces of Badgerness
in them whatsoever.

   use Badger::Class
       base      => 'Some::Other::Base::Module',
       accessors => 'foo';

So that's really what I was trying to get at. Badger's metaprogramming is
mostly just shortcuts straight to Perl 5's familiar OO with all the
fine-grained granularity that you can handle. But with it comes an implicit
assumption that you already know where you want to go and how to get there.

Moose operates at a slightly higher level of abstraction. To stretch the
analogy, it's like jumping in a taxi and letting the taxi driver find the best
route. In contrast, Badger gives you a bike (it's got a basket, a bell that
rings, and things to make it look good) and a map of some nice forest trails
with good foraging spots along the way.

Or, the summary that I've currently got in the FAQ is this:

   "To borrow the ice-cream analogy, if Perl 5's object system is vanilla,
    then Badger's is strawberry and Moose's is neapolitan with sprinkles
    and a sparkler."

So there you have it.  Moose in taxis, Badgers on bikes and ice cream
sundaes all round!  That's the best way I can explain it :-)

> The MOP is just a formalized API over the guts of Perl 5. Take a look  
> at Class::MOP::Package, it is simply an API over ugly typeglob code.  

Yes, I've had a good play around with Class::MOP and it's a very elegant
wrapper around a nasty mess.  Badger::Class does some of the same things,
although the emphasis is on doing the things that I need doing for Badger,
(which includes a fair bit of class metaprogramming) rather than provide a
complete metaprogramming framework.

> Class::MOP::Attribute is just a way of modeling a class attribute.  
> Class::MOP::Instance simply provides an API for constructing an  
> instance and providing access to it's slots. Class::MOP::Class brings  
> this all together.

This is where Class::MOP, and by extension, Moose, really shine, IMHO.
The whole metaprogramming model is well defined, properly organised and
nicely extensible.  I've already got plans to refactor Badger::Class
into something that's a bit more modular and I'll certainly be drawing
inspiration from Class::MOP and Moose (and/or trying to bolt it onto the
side of Class::MOP/Moose if possible)

>>     * Moose is more framework, Badger is more toolkit.
> 
> Actually I disagree here, a Framework is typically something which is  
> partially complete, but needs your code to fill in the rest in order  
> to become a full application. Catalyst is a framework, Ruby on Rails  
> is a framework, etc etc. Moose is not a framework, it is completely  
> stand alone and your code doesn't fill in the missing pieces, instead  
> it /uses/ Moose to build it's own thing.

Yes, you're right, although it was only intended to be a relative comparison
between the two. s/is more/is *slightly* more/g to get a better idea of what I
was thinking.

I meant it in the sense of 'use Moose' being a buy-in to all the Moose
goodness. You get the extra keywords, the Moose::Object base, and so on.
In return you have to accept the way that Moose does things OO-wise and put
a certain trust the route that the taxi driver takes.  But it is a complete
system. In contrast, Badger is more like a box of bits and some self-assembly
may be required.

> Again, I disagree, Moose is /made/ of metaprogramming, like you and  
> are a made of meat.

I'm not sure I agree with your disagreement, but that's may be down to my
broad use of the term "metaprogramming".

Let me try again:

    Moose is a post-modern object system which makes extensive use of
    metaprogramming.

    Badger provides a modern object system (among other things) which uses
    some metaprogramming.

> Well, yes and no, the syntactic sugar of Moose does a lot of  
> metaprogramming for you, so in that sense yes, using Moose is  
> metaprogramming. However I don't think that is what you meant.

No, that *is* what I meant to mean.  But then I would include any kind of
generative programming under my general umbrella of metaprogramming
(your umbrella may be different of course).  So all of the following would
be kept dry by my all-inclusive definition:

       has 'foo' => { ... }              # let Moose generate a method

       use Badger::Class                 # let Badger do it
           accessors => 'foo';

       use base 'Class::Accessor';       # let Class::Accessor do it
       __PACKAGE__->mk_accessors('foo');

> I actually tend to discourage people from doing too much  
> metaprogramming with Moose. If you have to reach for ->meta in your  
> Moose based app, there is a good possibility you are doing something  
> wrong and/or trying to be too clever. 

Yes, the deeper kind of metaprogramming (what I think of as extending
the metaprogramming framework, er, I mean toolkit) is not something for
day-to-day use.  My rule of thumb is to wait until I've done something
half a dozen times before trying to generalise it.  And even then, creating
a parameterised method, a mixin or common subclass is often the simpler
approach anyway.

> This of course is not counting the MooseX:: extensions I have  
> written, which almost always use ->meta. But those are different  
> because they are extensions of Moose and therefore usually involve  
> metaprogramming.

Yes, exactly.  Modulo our slightly different definitions of where the
metaprogramming stops and the metaprogramming-programming starts :-)

> I wont get into the dependencies are bad/good debate cause honestly I  
> think it is a holy war with no clear winner. 

I'm perhaps more sensitive to CPAN dependency hell than most because I do a
fair bit of work with designers who typically don't like using the command
line and rarely have the developer tools installed on their shiny Macs (hence
no C compiler, no File::HomeDir, no Bundle::CPAN, and all kinds of pain). For
my sins, I also have to contend with all the pesky people who want to run TT
on their ISPs who steadfastly refuse to install any CPAN modules.

But regardless of that, I'll take a CPAN shell and the occasional 'force
install' any day. So I wouldn't go as far as giving it Holy Way status. For
all its faults, CPAN is the clear winner most of the time. It's just that
there are some races which CPAN can't enter.

The fact that the core Badger modules are dependency free *is* a feature,
but only because of the niche that it occupies.  It doesn't follow that it's
a good feature in general for CPAN distributions.  In this specific case,
being dependency free is more-or-less a hard requirement because it's what
I'm building TT3 on (and a few other things).

> However, that said we do have Mouse (http://search.cpan.org/dist/ 
> Mouse/) which has minimal dependencies and can fit into the smaller  
> spaces where Moose can't.

Neat!  I wasn't aware of that (how *do* people keep up with CPAN these
days?).  I'll check it out some more.

> And Moose I am sure would play nicely with Badger. 

Yes, they both play together quite well.  I've only tried the basics of
creating objects using both Badger and Moose, but they both seem quite
happy in each other's company:

http://badgerpower.com/svnweb/Badger/view/trunk/badger/t/misc/moose.t

> As for world domination (muhahahaha), 
                            ^^^^^^^^^^
Come on, admit it.  You've been practising that haven't you?  :-)

> Moose is in no way shape or form still "experimental". 

Yes, I totally missed the boat here.  My apologies.  In the back on my
mind I remembered reading about your first attempts at implementing the
Perl 6 object model in Perl 5 (which were quite experimental at the time
if I recall correctly).  I didn't appreciate quite how far "modern"
Moose had come since then.

All the best
Andy




More information about the templates mailing list