2009-05-31

Moose's MOP: Ecosystem

Methods

Previously, we saw that metaclasses hold information about classes. We learned that classes know how they relate to other classes and that they know all of the method names they can accept. In most object-oriented languages that even expose a meta-model, this is as deep as it goes. In CLOS and Moose, the meta-model is much more elaborate. Instead of only metaclasses, we have many distinct kinds of meta-level classes.

$ my $meta = DateTime->meta
Moose::Meta::Class=HASH(0xd84dd4)

$ my $method_metaobject = $meta->get_method('ymd')
Moose::Meta::Method=HASH(0xd3f830)

We have a new meta-level class here, one that represents a method. DateTime's ymd method corresponds to this instance of the Moose::Meta::Method meta-level class. Let's check out what this method metaobject can do. As you've already seen, the way to see what methods $object can accept is $object->meta->get_all_method_names.

$ $method_metaobject->meta->get_all_method_names
$ARRAY1 = [
            'original_fully_qualified_name',
            '_inline_throw_error',
            'dump',
            # ... elided ~16 methods ...
            'execute',
            'original_package_name',
            'fully_qualified_name'
          ];

These are certainly applicable to methods. Of course you can execute a method. fully_qualified_name tells you the full name of the method. original_fully_qualified_name is different when the method came from a role.

You don't normally see this method metaobject. It's just there behind-the-scenes to do bookkeeping on your behalf. The only way to even see this object is if you peer into DateTime->meta to find it. You (and your users) never have to interact with metaobjects if you don't want to. However, I hope to show that you can benefit greatly by interacting with metaobjects.

To be honest, methods aren't particularly interesting because all you can do is invoke them. Perl doesn't have anything like method signatures for you to inspect (unless you use an extension), or generic functions for you multi-dispatch over (again, unless you use an extension). By default, method metaobjects are just glorified functions.

Attributes

Moose's attribute protocol is far more interesting than its method protocol. The metaclass for attributes is just as rich as that of classes.

$ my $attribute_metaobject = $meta->get_attribute('time_zone')
Moose::Meta::Attribute=HASH(0xda709c)

$ $attribute_metaobject->meta->get_all_method_names
$ARRAY1 = [
            'verify_against_type_constraint',
            'is_default_a_coderef',
            '_coerce_and_verify',
            # ... elided ~83 methods ...
            'has_type_constraint',
            'remove_delegation',
            'get_write_method'
          ];

Here we have another new meta-level class, Moose::Meta::Attribute. Given that Moose attributes support type constraints, defaults, type coercion, and delegation, these methods for attributes should come as no surprise. We can invoke some of these methods to, say, get a better idea of how DateTime works.

$ $attribute_metaobject->get_write_method
set_time_zone

$ $attribute_metaobject->has_type_constraint
1

$ $attribute_metaobject->type_constraint
DateTime::TimeZone

$ $attribute_metaobject->_coerce_and_verify('America/New_York')
DateTime::TimeZone::America::New_York=HASH(0xcd3730);

$ $attribute_metaobject->_coerce_and_verify('Europe/New_York')
Runtime error: The timezone 'Europe/New_York' could not be loaded, or is an invalid name.

Perl's builtin OO doesn't really have attributes. At best, programmers write constructors and accessors to interact with each instance's data structure. Moose users have benefitted greatly lot from the reification of attributes. Declarative features like type constraints and lazy defaults improve the robustness and maintainability of code.

Making these declarations inspectable through the metaobject protocol permits even greater reuse. We only have to declare the type constraint of an attribute once to permit validation in both the constructor and all associated accessors. We also open that attribute up for other code (such as MooseX modules, and your own custom code) to make informed decisions about how to interact with that attribute. For example, a web application's form builder can inspect an attribute's type constraint to perform validation of a field through AJAX. As we'll see next time, an ORM could inspect type constraint and other declarations to generate a complete database schema.

There are several other metaclasses in Moose: roles, type constraints, type coercions, instances, and even modules and packages. However, since most inspection (and as we'll see later, extension) happens for attributes and classes, those are where our focus will remain. The principles we cover will apply equally to these other metaclasses.

2009-05-23

Moose's MOP: Browse

Use Case

I realized that it would be very instructive to accompany each of the articles in the Moose's Metaobject Protocol series with a solid use case. While metaclass programming isn't particularly difficult (remember, it's just OOP), it takes some experience to fully wrap your head around it. I also want to demonstrate that MOPs certainly have their uses, since advanced techniques might come off as solutions looking for problems.

Today I present a very simplistic class browser. It shows you a structured view of a class's superclasses, subclasses, and methods. You can easily see which methods come from which classes, and which methods override superclass methods. The browser is written in about a hundred lines of Perl (plus comments). It uses only the MOP concepts you've been introduced to, though admittedly some of the details are different. I would've written the previous article a little differently had I this idea sooner!

I encourage you to follow along with a copy of the code from GitHub. You can also look at a static copy for DateTime.

Real Working Code!

sub display_class {
    my $class = shift;

    Class::MOP::load_class($class);

    return html {
        body {
            h1 { $class }

            h3 { 'Superclasses' };
            display_superclasses($class);

            h3 { 'Subclasses' };
            display_subclasses($class);

            h3 { 'Methods' };
            display_methods($class);
        }
    }
}

This is the outermost function for class-browsing. It takes the name of a class (such as DateTime::Infinite) and returns an HTML document. This program uses the novel Template::Declare for the tedious work of generating correct HTML.

We start by loading the class. We're letting the user inspect any class, so we need to ensure that the class is loaded and ready for inspection. We then divide the work of displaying the class into three sections: superclasses, subclasses, and methods.

sub link_class {
    my $class = shift;
    a { href is "/$class"; $class }
}

link_class is just a little helper function to render a class name as a hyperlink. This adds some dynamicism to the application, letting you click through to see the hierarchy from other classes' perspectives.

sub display_superclasses {
    _display_hierarchy($_[0], 'superclasses');
}

sub display_subclasses {
    _display_hierarchy($_[0], 'direct_subclasses');
}

It turns out that displaying superclasses and subclasses is the same operation. The only difference is which method is called, so I factored out that logic into the _display_hierarchy helper function.

These two functions take a class name as a parameter. Each calls the helper function with that class name and an additional argument, a method name. The method will be called on the class's metaclass to generate the next layer of inheritance. This helper function is called recursively for each class in the hierarchy. The final result is a nicely formatted tree of classes. As an example, here's the end result for DateTime's subclasses:

sub _display_hierarchy {
    my ($class, $method) = @_;

    my $meta = Moose::Meta::Class->initialize($class);
    my @classes = $meta->$method
        or return '';

    ul {
        for my $other_class (@classes) {
            li {
                link_class($other_class);
                _display_hierarchy($other_class, $method);
            }
        }
    }
}

This function is the workhorse of displaying sub- and superclass trees. It collects the classes (based on the method name that's passed in) that represent the next layer of hierarchy, displays each one, then recurses.

You may not have seen $object->$method before. Most modern OOP languages permit this simple form of reflection. Instead of invoking the literal method name, you invoke a method name which is in a variable. Simple and handy, but it's a real pain in C++ and Java.

This reflection is perfect for us, since we display superclasses and subclasses the same way. For the list of direct superclasses, we call $meta->superclasses. For direct subclasses, we call $meta->direct_subclasses. Yes, this naming inconsistency does suck!

The rest of the function is pretty straightforward. Multiple inheritance would muck up the display, but oh well. That's more a fault of HTML being unequipped to sanely display graphs than it is a fault of the meta-model, which copes well enough with MI.

sub display_methods {
    my $class = shift;
    my $meta  = Moose::Meta::Class->initialize($class);

    ul {
        my %seen_method;

        for my $superclass ($meta->linearized_isa) {
            my $super_meta = Moose::Meta::Class->initialize($superclass);

            li {
                link_class($superclass);
                ul {
                    for my $method (sort $super_meta->get_method_list) {
                        if ($seen_method{$method}) {
                            li {
                                style is 'text-decoration: line-through';
                                $method
                            }
                        }
                        else {
                            li { $method }
                        }
                        $seen_method{$method} = 1;
                    }
                }
            }
        }
    }
}

This is a big one. There are three major things happening here. We're iterating over the linearized_isa of the class. This is the order of classes that method dispatch takes (with duplicates removed). It's the tree of all superclasses, but flattened into a list.

Secondly, we're iterating over each class's get_method_list. This is similar to the get_all_method_names that you've seen before. The difference is that get_method_list does not include inherited methods. Only methods defined in that particular class are included.

Thirdly, we're treating methods that we've seen already as special. When we see a method for the first time, we display it normally. When we see that method again, we display it with a strike-through. This indicates that the method was overridden. We know the method was actually overridden because we iterate over the classes in method-resolution order.

The net effect is that we show every level where methods were defined and overridden in the overall class hierarchy. This could be useful for learning an unfamiliar project's design, or in hunting down a strange regression (caused by an accidental override).

That is it for the class-browsing aspect of this. What remains is the business of hooking this class browser up to the web. For this I use the excellent HTTP::Engine module.

sub handle_request {
    my $request = shift;

    my ($class) = $request->request_uri =~ m{^/([\w:]+)$};
    $class ||= 'DateTime';

    my $body = display_class($class);
    HTTP::Engine::Response->new(body => $body);
}

This is a callback for HTTP::Engine. HTTP::Engine passes to the callback a request object, and we're expected to return a response object. We dispatch on the request's request_uri, which will just be the class name.

my $engine = HTTP::Engine->new(
    interface => {
        module          => 'ServerSimple',
        args            => { port => 1978 },
        request_handler => 'main::handle_request',
    },
);
$engine->run;

And finally we let HTTP::Engine do its thing.


Hopefully this has demonstrated a real (if simplistic) use of the information a metaclass contains. This could be extended into a real class browser which also includes documentation (extracted with your POD module of choice), each method's code (thanks to B::Deparse), a lint report of that code (using the wonderful Perl::Critic), and any other information you might have handy. None of these things actually relate to the metaobject protocol, so they weren't included in the code. However, the metaobject protocol could serve well as the basis of such a system.

2009-05-17

Moose's Metaobject Protocol: Foray

To avoid frivolous toy examples, this series of articles draws heavily from Perl's excellent DateTime module. You really should be using this module, but even if you aren't, it has a very clear API so you should have no trouble.

Breaking in

If you use Moose, you've probably had it drilled into your head that you always need to "make immutable". Maybe you don't know what it does or means (beyond that it makes your code faster), but you've written it how many times now?

__PACKAGE__->meta->make_immutable;

Just what the hell is __PACKAGE__->meta (a.k.a. DateTime->meta1) anyway? Well, print it out (or inspect it with Devel::REPL as I do here).

$ __PACKAGE__->meta
Moose::Meta::Class=HASH(0x9021fc)

So DateTime->meta is an object of class Moose::Meta::Class. Moose::Meta::Class is, unsurprisingly, a metaclass.

A metaclass is a class for classes. Every class is an instance of a metaclass.

Breathe in. Breathe out.

In the exact same way that objects are instances of classes, classes are instances of metaclasses. Metaclasses are classes. Classes are objects.

To be clearer, here's a table of classes, and instances of those classes.

In the end, it's all just object-oriented programming. You lot are quite adept at OOP, so you have nothing to fear! I promise I'll go slowly.

So we have this Moose::Meta::Class object. Let's call some methods to see else this unfamiliar beast knows.

> DateTime->meta->superclasses
Moose::Object

> DateTime->meta->subclasses
$ARRAY1 = [
            'DateTime::Infinite::Future',
            'DateTime::Infinite::Past',
            'DateTime::Infinite'
          ];

> DateTime->meta->get_all_method_names
$ARRAY1 = [
            'compare',
            '_calc_utc_components',
            'date',
            # ... elided ~173 methods ...
            'time_zone',
            'subtract_duration',
            'mjd'
          ];

Interrogation

As you can see, metaclasses hold a treasure trove of information about a class. You probably already know how to get the superclasses of any Perl class, but how the hell did Moose know what the subclasses of DateTime are?2 On second thought, who cares! This is simple encapsulation, a familiar principle of OOP. The relevant question is not how? but what?

Consider the problem of getting the names of all methods DateTime accepts 3, using just vanilla Perl OO. You have to grab a reference to DateTime's symbol table, iterate over the key-value pairs, plucking out the name of any typeglob with a CODE entry. All told, it ends up looking like this:

my $symbol_table = \%DateTime::;
for my $name (keys %$symbol_table) {
    my $glob = $symbol_table->{$name};
    next unless *{$glob}{CODE};
    say $name;
}

Generalize that to work for any class. Oh, and make it crawl up the inheritance hierarchy, since an object can accept its superclasses methods. Now you've got a seriously hairy piece of code. I'd much rather stick with __PACKAGE__->meta->get_all_method_names. Of course, you need Moose to use this nice API...

I have a confession to make. DateTime is not written with Moose at all.4 It's written in plain Perl OO. To interrogate DateTime for its inheritance hierarchy and methods, I didn't have to port it to Moose, either. Moose will inspect plain Perl classes to pull out as much information as it can. In fact, Class::MOP (Moose's foundation) has something very close to that hairy bit of code in it. Class::MOP's symbol table crawling is written in XS, so it's also faster than the Perl equivalent.

Though Moose is quite happy to work with vanilla Perl classes, the metaclass will have plenty more information if the class was written with Moose. Since vanilla Perl has no notion of attributes — just methods that poke into the instance's data — Moose can't learn anything about the attributes of a vanilla class.


Metaclasses have plenty more to offer, but we need more context. Next time, we'll look at what other objects live in the Moose ecosystem. In the meantime, snoop around some of your classes' metaclasses.5 If you want to inspect a class that doesn't use Moose, you can use the following to generate a metaclass for your class:

use Your::Module;
use Moose ();
my $meta = Moose::Meta::Class->initialize('Your::Module');
print join ', ', $meta->superclasses;

Footnotes

  1. This is a bit of a fib. DateTime has no ->meta method. I'll explain what's up soon! (back)
  2. The builtin mro::get_isarev function (and its MRO::Compat analogue for 5.8) is exactly for this purpose. In 5.8, every package must be searched, but in modern perls it is cached. (back)
  3. There are several reasons you might want to inspect a class's methods. The foremost use, and probably most nefarious, is to look at what methods you can call when you're using that particular class. You should read its documentation instead! A better use is in making sure you're not accidentally overriding any methods from a subclass. (back)
  4. Yet! There are vague mumbles of a possible DateTime 2 that would probably use Moose. Dave Rolsky is a core Moose developer, after all. (back)
  5. If you have no classes handy, you can earn bonus points by poking at Moose::Meta::Class's metaclass, or Moose::Meta::Class's metaclass's metaclass! (back)

2009-05-12

Parameterized roles

There has been a lot of good discussion about roles lately, due to chromatic and Ovid. We're starting to attract people from outside of not only the Moose and Perl 6 communities, but outside of the greater Perl community. I'm thrilled, because roles are an excellent complement to inheritance, and deserve to be widely adopted.

In The Why of Perl Roles, chromatic quickly describes parametric roles, also known as parameterized roles. A parameterized role is a role whose application can be customized. Such a role accepts parameters at composition time to alter its behavior any way it wishes. This increases the flexibility of the role, fostering opportunities for greater code reuse.

Example

My NetHack bot TAEB has a TAEB::Action::Role::Item parameterized role. It accepts one parameter, a list of names for item attributes. These attributes (which specify a type constraint of NetHack::Item and a custom "provided" trait) are added to the class upon composition. The role provides a few other things as well: a current_item attribute and a method for dealing with missing-item exceptions. The current_item attribute's value is a lazy default, pointing to the first item in the parameterized list of attribute names. This is an application of the well-trodden Don't Repeat Yourself principle — each class does not have to repeat its item attribute names. Parameterized roles are pretty good enablers of DRY!

Since NetHack is a very item-oriented game, this role has many consumers. The TAEB::Action::Dip consumer specifies item and into for the list of attribute names, so it gets those two attributes (as well as whatever else the role provides). Likewise, TAEB::Action::Wield gets a weapon attribute. Most of the consumers specify nothing for this parameter, so they get the default list of item. Consuming a parameterized role without specifying any parameters is syntactically identical to applying vanilla roles, which is a (minor) convenience.

In older versions of the code, TAEB::Action::Role::Item was not parameterized. This role always gave you a single attribute named item. The into attribute of TAEB::Action::Dip was a second-class citizen. There are a number of ways a second-class item attribute can go wrong, such as not having a type constraint, or not being visible to current_item's lazy default. It also prevented us from renaming the item attribute to, say, weapon for TAEB::Action::Wield. We drastically improved the code's clarity and expressiveness by parameterizing the role.

Design

In December, I wrote the MooseX::Role::Parameterized extension. I had grown tired of people (primarily myself!) putting off coding because the proper implementation of whatever demanded parameterized roles. I am not exaggerating:

  • hoping once we have parameterised roles we can do that
  • you will have [...] parameterized roles which will allow such things very easily
  • let's see what the parameterized roles end up giving us
  • once we have parameterised roles I shall be providing one to produce all three trivially
  • parameterized roles will probably do all that you want
  • I'm hoping parameterised roles will let me get rid of that
  • what's the status of parameterized roles? I could really use them
  • has there been any progress on parameterized roles?
  • we don't have parameterized roles yet?!
  • are parametrized roles somewhere on the roadmap
  • that sounds like parameterized roles, which someone should implement some time

Designers of parameterized role systems (perhaps three or four of us by now?) must decide how much freedom each role is given. How much parameterizability is permissible? In MooseX::Role::Parameterized, and Perl 6's parametric roles, there are no limitations. You can parameterize anything.

Maximizing your user's freedom is certainly laudable, but I am not convinced that it is completely beneficial. There is value in stricture, for example, to improve the efficacy of class/role browsers. I will leave such meditations for the next parameterized role designer. At least for Moose, we have several more chances to get the design right; there are plenty of other namespaces available on CPAN: MooseX::Role::Parametric, MooseX::Role::Parameterizable, MooseX::Role::Parameterised, and so on.

MooseX::Role::Parameterized works, in short, by executing a procedure for each role application. This procedure takes as an argument a "parameters" object. It is executed in the dynamic extent of an anonymous role metaobject. All of the usual role keywords (has, around, requires, etc) operate on this anonymous role. The procedure may use the "parameters" object in building up the role using these keywords.

This is also a reasonable description of how vanilla roles are created, from a backstage point-of-view. The primary difference is that the procedure is only ever executed once for vanilla roles, whereas it is executed potentially many times for parameterized roles.

This difference is precisely what makes the syntax of MooseX::Role::Parameterized slightly unusual, at least in the context of core Moose:

package Counter;
use MooseX::Role::Parameterized;

parameter name => (
    isa      => 'Str',
    required => 1,
);

You explicitly specify which parameters the role can take. This improves introspectability, and makes it clear (if you've written Moose code) how to specify that a parameter is required, or has a type constraint; default value; or predicate, etc.

role {
    my $p = shift;
    my $name = $p->name;

    has $name => (
        is  => 'rw',
        isa => 'Int',
    );

    method "increment_$name" => sub {
        my $self = shift;
        $self->$name($self->$name + 1);
    };
};

The role keyword exists for specifying the aforementioned role-building procedure. This is the unusual syntax. MooseX::Declare could improve upon this a lot, once some tuits are spent.

The method keyword is required (instead of the usual sub name { ... }) because Perl handles named, inner subroutines in a way that is not useful for this design. The details are many, but mostly unimportant. The salient point is that such subroutines do not properly close over $p, rendering them unparameterizable. The anonymous subroutine passed to method can properly capture $p. The method keyword has a fringe benefit over sub in easing the parameterization of the method's name.

Current and future work

I recently fixed a bug in MooseX::Role::Parameterized that prevented parameterized roles from participating in role summation. Role summation is what provides protection against method name conflicts at compile time, among other things. Being unable to partake in composition limited parameterized roles' usefulness. I'm very happy to have fixed the one crippling issue with this extension.

The fix involved a bait-and-switch hook in Moose's role summation code. The hook is fairly contrived, but it might possibly be useful to other role extensions.

I'm currently noodling around with the partial application of parameterized roles. Currently, if a role consumes a parameterized role, it must specify all of the required parameters. In fact, the parameterized role is bound (the procedure is executed) at that moment. MooseX::Role::Parameterized does not care about whether it is being consumed by a class or a role. Instead, I'd like the consuming role to be able to specify some of the parameters. Any unbound parameters will be parameters in the composite role.

The only blocker stopping me from working on partial application of parameterized roles is a use case. Adding complexity for no reason is clumsy.

2009-05-03

Perl-Critic-Dynamic-Moose

In my previous post, I wrote "What we need is a set of Perl::Critic policies for Moose." I've put my money where my mouth is and implemented five of them.

CPAN?

Perl::Critic::Dynamic::Moose is not on CPAN yet because Perl::Critic does not currently distinguish between "safe" (static) and "possibly unsafe" (dynamic) policies. Perl::Critic usually works by statically inspecting some Perl document. This allows users to critique untrusted code. Most of the policies I wrote are dynamic. These dynamic policies work by executing the code to be critiqued, then analyzing any new metaclasses that were created. While this limits their utility to analyzing only trusted code, this dynamicism does magnify analytic potential. Instead of inspecting PPI DOMs, these policies can inspect any and every metaobject in the system. A large part of why the metaobject protocol exists is for sane and consistent inspection, so these dynamic policies work well.

Anyway, Perl::Critic uses Module::Pluggable to unconditionally load every module in the Perl::Critic::Policy namespace. It would be harmful to add unsafe modules to that namespace, so I am going to make sure that a new Perl::Critic that runs only safe policies is released before I begin publishing dynamic policies. It should be a very simple matter of programming...

Perl::Critic::DynamicMoosePolicy

Perl::Critic::DynamicMoosePolicy is a base class for dynamic Moose policies. It's written, of course, with Moose (extending Perl::Critic::Policy using MooseX::NonMoose). It does the things that every dynamic Moose policy needs to do:

  1. Compile the document being analyzed
  2. Find all the metaclasses that were just created
  3. Call your policy's violates_metaclass for each metaclass

Ideally, policies would just implement violates_metaclass and it would all just work. However, there are (seemingly always) some details.

Since this analysis is entirely divorced from the source code, it is impossible (in general) to provide a useful file and line number pointing to the violation. Instead of making each policy provide an arbitrary location in the document for violations, DynamicMoosePolicy just does it for you. If you're combining PPI and metaobject inspection, then you can of course provide a more useful violation location when you have one. Instead of a location, I recommend that you be as detailed as possible in your error report (include the class name!)

Perl::Critic::DynamicPolicy runs each policy in a forked process, to prevent the analyzed code from mucking with the analyzing process's operation. Dynamic policies communicate violations with perlcritic via a parent-child pipe, with all violation objects frozen and thawed with Storable. This means your violations cannot include code references (such as those created by Scalar::Defer), or anything else that Storable cannot handle. I spent a while scratching my head trying to figure out what was causing this error:

Cannot restore overloading on HASH(0xc42f0c) (package <unknown>) at blib/lib/Storable.pm (autosplit into blib/lib/auto/Storable/thaw.al) line 415

It turned out to be the PPI::Document object that the violation was using as an arbitrary location. Specifying the first element in the document made that error go away. I'm not going to ask too many questions here, just smile and nod.

augment/inner

Policies may define a applies_to_metaclass method that filters for only the metaclasses that sensibly apply. I utilized the rarely-seen augment and inner for this method (as well as default_themes). The default implementation of applies_to_metaclass is:

sub applies_to_metaclass { 'Class::MOP::Class', inner() }

If the policy wants to analyze only role metaobjects, then they can ignore the method's inverse nature by writing the standard:

sub applies_to_metaclass { 'Moose::Meta::Role' }

If the policy wants to analyze only class metaobjects, then they do not have to augment this method. The parent's inner in such cases is a no-op, returning the empty list.

However, if a policy wants to analyze both classes and roles (which I suspect happens when the actual subject for analysis is attributes and methods), the policy can include:

augment applies_to_metaclass => sub {
    'Moose::Meta::Role'
};

augment signals that the method resolution order descends the class hierarchy: grandparent -> parent -> child. inner calls the next most specific method. The end result of the above is the list ('Class::MOP::Class', 'Moose::Meta::Role').

PCP::DynamicMoose::ProhibitPublicBuilder

I wrote this policy first because I knew this area of the metaobject protocol well. Any problems were likely caused by the fork-and-eval side, not the inspection side. Pick your battles.

Each attribute can have a builder method that is called to provide a default value for that attribute. This facilitates changing this default value by subclassing and role application better than the default option does.

The inspection is straightforward. For each attribute on the class (or role), ensure that, if it has a builder, its name begins with an underscore. The only reason for this is because I sometimes forget my builders would be public API, when they generally should not be.

Handling roles for this policy sucks because attributes in roles are just inert hash references. Since there have been threats to improve roles to make use of real objects, I will elide handling roles here for brevity.

for my $name ($meta->get_attribute_list) {
    my $attribute = $meta->get_attribute($name);

    my $builder = $attribute->builder
        or next;

    if ($builder !~ /^_/) {
        my $desc = "Builder method '$builder' of attribute ...";
        push @violations,
            $self->violation($desc, $PL);
    }
}

get_attribute_list returns the names of attributes defined in this local class. Inherited attributes are not considered. I figure you would be running these policies on every class and role in your project anyway.

PCP::DynamicMoose::RequireMakeImmutable

Next up is another easy policy. This is by far the simplest. It performs one simple check on the metaclass (is_immutable).

Elliot Shank wrote Perl::Critic::Policy::Moose::RequireMakeImmutable, a static policy that checks for __PACKAGE__->meta->make_immutable. There is room enough for both a static and a dynamic policy. Not every class can make_immutable in that specific way. Class::MOP, for example calls make_immutable on all of its classes at once. In other cases, the programmer may have accidentally forgotten to call make_immutable on a dynamically-generated class.

As with most static policies, PCP::Moose::RequireMakeImmutable admirably handles the common cases, but might not handle strange corner cases well. Dynamic policies can fill such gaps.

PCP::Moose::RequireMethodModifiers

Moose provides a rich set of method modifiers:

  • before
  • after
  • around
  • override / super
  • augment / inner

There's a method modifier for just about every way you'd want to interact with homonymous methods. If you define a method that is already present through inheritance, then this policy asks you to explicitly use a method modifier. This way, you might be able to catch accidental method overrides. If you do want to completely replace an ancestor's method, then you can use override without calling super. That too provides some typo protection, since override will confirm that an ancestral method exists.

This is the first novel dynamic policy; the previous policies could be implemented statically, but this one would have little chance. Let's walk through its entire violates_metaclass.

sub violates_metaclass {
    my $self  = shift;
    my $class = shift;

    my @violations;

This policy does not apply to roles, and does not require Moose-specific features (such as, well, roles), so it can use the default applies_to_metaclass. We also need a place to store the list of violations, since many methods could be implicitly overriding inherited methods.

    for my $name ($class->get_method_list) {
        my $method = $class->get_method($name);

We iterate over the class's local methods. get_method_list and get_method do not look at inherited methods at all. Methods brought in from locally-consumed roles are also included in this list — this is a feature called flattening. (If in your code you do want to consider inheritance, use get_all_methods and find_method_by_name)

        # override and augment modifiers are always fine.
        next if $method->isa('Moose::Meta::Method::Overridden')
             || $method->isa('Moose::Meta::Method::Augmented');

This quickly excludes two of the five method modifiers. Such modifiers have their own metaclasses for various reasons, not least of which is our usage here: to allow detection that the method is just a modifier.

        # Since we can implicitly override and wrap in the same class, we
        # need to be a little more careful here.
        if ($method->isa('Class::MOP::Method::Wrapped')) {
            my $orig_method = $method->get_original_method;
            next if $method->associated_metaclass->name
                 ne $orig_method->associated_metaclass->name;
        }

Handling the other three method modifiers (before, after, and around) is trickier. You can both define a method and wrap it with these three modifiers in the same class. This is useful for, say, running code before an attribute's clearer.

If the method we're wrapping is from a different metaclass, then we must be wrapping an inherited method, which is fine. If we're wrapping a local method, then we need to peel off a layer. We do not need peel off many layers in a loop, since multiple local modifiers coalesce into one layer.

        # Generated methods
        next if $method->isa('Class::MOP::Method::Generated');

This one worries me a little. Generated methods are those created for you by Moose. Examples include accessors, new (but only after a make_immutable), and delegation methods. Since these methods are generated for you, you have no way to indicate that they are overriding an inherited method.

Because you can certainly accidentally override an inherited method with a generated method, this check should be optional.

        # XXX: this freaking sucks
        next if $name eq 'meta'
             || $name eq 'BUILD'
             || $name eq 'DEMOLISH';

This "freaking sucks" for two reasons. meta is installed into your local class by Moose. That's fine (though we will eventually support renaming or excluding it). The metaclass of the meta method is not Class::MOP::Method::Generated like it should be, it's Moose::Meta::Method. This misclassification makes it look like a regular method defined by the user. meta would receive special consideration even if it were properly labeled as a generated method, because every class gets its own.

The other reason this sucks is the hardcoded BUILD and DEMOLISH. These are special methods with unusual dispatch semantics. Every BUILD and DEMOLISH method is called in your hierarchy automatically; there is no choice about redispatching to your inherited method. This is a useful feature, because too often do we forget to do this which can silently mess up initialization or destruction. Anyway, this list of special methods should be inspectable from the metaclass, and given their own Class::MOP::Method subclass.

        my $next = $class->find_next_method_by_name($name);

        # Adding new methods is always fine.
        next if !$next;

Simple enough, right? It finds the inherited method by a given name, if any. If there is none, then it must be an entirely new method with a unique name, which is perfectly fine.

        push @violations, $self->violation(
            "The '$name' method of class " . $class->name
          . " does not use a method modifier to"
          . " override its superclass implementation.",
          $EXPL);
    }

    return @violations;

That's all. We've exhausted all the acceptable ways for a method to interact with its super-methods, so what remains must be a violation.

PCP::DynamicMoose::ClassOverridesRole

Ahhh, the violation that launched a thousand comments. I've already summed up the what and the why of this violation.

I wrote this policy today. It required keeping track of more metadata in Moose. There exist a handful of Moose::Meta::Role::Application classes, which track, among other things, an application of a role to a class. We were creating these objects but then discarding them once the application was completed. Instead, we now keep track of them. Progress!

Let's walk through this implementation as well.

sub violates_metaclass {
    my $self  = shift;
    my $class = shift;

    my @violations;

Familiar enough.

    for my $application ($class->role_applications) {
        my $role = $application->role;

Every time you consume a role with with, it creates an object of class Moose::Meta::Role::Application::ToClass and adds it to your metaclass. This object has, among other things, a role attribute.

As an aside, it might be interesting to make the does method return these role application objects. They're certainly true values, and there is precedent: the true value that can returns is a code reference.

        for my $method ($role->get_method_list) {
            next if $application->is_method_excluded($method);
            next if $application->is_method_aliased($method);

We need to consider every method in the role. If a method has been excluded or aliased, then that is explicit overriding, so the violation does not apply.

To allow for this explicit override is exactly why I needed to add role-application tracking to Moose, and why I could not implement this policy until today.

            my $method_object = $class->get_method($method)
                or next;

We inspect the method's metaobject from the class. Again, we use get_method and not find_method_by_name because we do not want to consider inheritance. At this point, the method is definitely there, since it hasn't been excluded. What matters now is whether the role provided it or if the class did.

            if ($method_object->isa('Moose::Meta::Role::Method')) {
                next if $method_object->body
                     == $role->get_method($method)->body;
            }

Methods of class Moose::Meta::Role::Method were originally pulled in from a role. If the method was not provided by a role, then it was a silent class override.

The inside statement makes sure that the method was provided from the correct role, not just any role. If you sequentially apply Role::Dog then Role::Tree, you would want to know about the implicit override of Dog's bark method with Tree's.

The inside statement also attempts to handle the method coming in from the same role multiple ways. Suppose you have a Foo::Basic role with a method kerplow that is consumed by both the Foo::Advanced and Foo::Safe roles. A class that sequentially consumes Foo::Advanced then Foo::Safe would get the kerplow method twice. But since it's the exact same implementation (i.e. the same code reference), it's not worth warning about. I trust that the programmer will utilize Test::Perl::Critic, so that the divergence of the two kerplow methods would become a violation.

            my $class_name = $class->name;
            my $role_name  = $role->name;

            my $desc = "Class '$class_name' method '$method'"
                     . " implicitly overrides the same method"
                     . " from role '$role_name'";
            push @violations, $self->violation($desc, $EXPL);
        }
    }

    return @violations;

And that's it.

PCP::Moose::ProhibitMultipleWiths

I did write one static policy, which prohibits multiple calls to with in one package. Roles provide powerful and safe means of combination, but these are only activated if you actually combine the roles. If you sequentially apply multiple roles to a class, conflicting method names are silently ignored. This could possibly be fixed in the future, but for now we'll have to live with this behavior. At least we can now warn you about it.

This implementation is pretty simple as far as Perl::Critic policies go. I wrote this one as a static policy because PPI-based analysis is still very worthwhile. It actually would have been easier to implement dynamically. However, as I explained earlier, you would lose out the "location" of the violation — the second with.

Conclusion

I'm quite happy with how dynamic Moose policies are coming along. We now have policies for all the ideas I listed in my previous post. The only one I didn't cover, "Declaring new methods instead of using the many object construction hooks", was written quickly after my post by Elliot as a static policy.

As far as writing more policies, I think it's largely limited by creativity. I was able to write all the policies I wanted. There were some hairy bits, but mostly it was quite pleasant and a novel use of the metaobject protocol.

Ovid has submitted a bug to the Moose RT queue: Excluded methods should be added to 'requires'. I'm not sure I agree with his assertion that "since My::Class->meta->does('My::Role::Foo'), there's a promise that it will provide the &foo method". Since this may not make it into core Moose, I invite Ovid to write a policy covering this case. It would look quite similar to the ClassOverridesRole policy.

If you have ideas for more policies, or (better yet) would like help implementing them, get in touch with me! I'd be thrilled to help you out.

The code for the DynamicMoose policies is in Moose's git repository. Commit bits to this repo are handed out liberally, just ask in #moose on irc.perl.org. Providing a patch with your request will help your chances.

These policies have not been battle tested on large codebases, only on the microscopic class hierarchies in its test cases. I expect that DynamicMoosePolicy will need more work before it is all ready for wide use. Get your bug reports in before I move on to the next interesting project. :)