Stories
Slash Boxes
Comments
NOTE: use Perl; is on undef hiatus. You can read content, but you can't post it. More info will be forthcoming forthcomingly.

All the Perl that's Practical to Extract and Report

use Perl Log In

Log In

[ Create a new account ]

Journal of Stevan (6065)

Friday May 12, 2006
10:12 PM

Roles for Moose

I have just released Moose 0.09_01 (as well as Class::MOP 0.29_01 to go with it). It is a fairly significant update with a number of new features, the most interesting of them being a fairly complete implementation of Roles. For the most part the Role features are based on Perl 6 roles, but I have added a few features from the original Traits paper and some new ideas from the Fortress spec.

Roles in Moose look a lot like classes in Moose do, in fact almost all Moose class keywords are supported in Moose::Role. Here are some basic examples.

package Equality;
use Moose::Role;

requires 'equal_to';

sub not_equal_to {
    my ($self, $other) = @_;
    not $self->equal_to($other);
}

This is a basic Equality role, it requires any class which 'does' it to implement a method called 'equal_to', and then provides a 'not_equal_to' method to compliment it. The 'requires' feature is not from Perl 6, but from the original Traits paper, although Perl 6 mimics this with the 'method stub' feature.

package Molecule::Organic;
use Moose::Role;

excludes 'Molecule::Inorganic';

package Molecule::Inorganic;
use Moose::Role;

This is a feature from the latest Fortress language spec (the example is taken from there as well). Obviously a class would not want to 'do' both Molecule::Organic and Molecule::Inorganic roles, they are mutually exclusive. The 'excludes' keyword allows you to state that explicitly. This will assure than any class which 'does' Molecule::Organic (and all it's subsequent subclasses) will never be allowed to 'do' Molecule::Inorganic, and vice versa.

package CGI::Application::Role::RequestLogger;
use Moose::Role

before 'run' => sub {
    my $self = shift;
    warn '-' x 80;
    warn "REQUEST_URI: " . $ENV{REQUEST_URI};
    warn "Query Params:";
    warn "\t$_ => " . $self->query->param($_)
        for $self->query->param();
    warn '-' x 80;
};

Moose Roles also support the method modifiers as well. When a CGI::Application subclass 'does' this role, the 'run' method (which does all the dispatching) will be wrapped and a number of useful bits will be dumped to STDERR. Using it as simple as this:

package My::CGI::Application;
use Moose;

extends 'CGI::Application';
   with 'CGI::Application::Role::RequestLogger';

# ... now write your CGI::App subclass ...

Role now also have fairly sophisticated conflict detection mechanism as well. This allows roles to be combined together in a symmetrical way which will catch a number of compositional problems, and assure all requirements are met. We even handle mutually recursive roles like these:

package Role::Foo;
use Moose::Role;

requires 'foo';
sub bar { 'Role::Foo::bar' }

package Role::Bar;
use Moose::Role;

requires 'bar';
sub foo { 'Role::Bar::foo' }

package Foo::Bar;
use Moose;

# this will Just Work
with 'Role::Foo', 'Role::Bar';

This is similar to how Perl 6 will work, but once again, we diverge slightly. Moose roles can be combined (as above), and conflicts and requirements will be handled automatically, and they can also be combined manually with multiple 'with' keywords. This allows for a greater degree of control when you need it.

Roles are also showing a lot of promise at $work as an excellent plugin mechanism. This is where the 'requires' and 'excludes' features are coming in quite handy. They have the potential to prevent a whole class of composition issues which the usual 'mixin' based plugin methods are sometimes plagued with.

Some people have been experimenting with Roles to implement abstract classes and Java-style interfaces. The 'abstract' methods which a classes must implement can just be marked with the 'requires' keyword, and the class which 'does' the role has to implement them.

So, please give Moose 0.09_01 a try and play with Roles, and let me know what you think :)

- Stevan

Thursday March 23, 2006
09:17 AM

Moose, it's the new Camel

I have released a Moose onto CPAN :)

Moose is yet another way to write Perl 5 OO, however, this time it's different (it is, really, I swear). I have built Moose on top of the metaclass framework Class::MOP, so all the "magic" that Moose does is built upon the solid theoretical foundations found in other object systems like CLOS, Smalltalk 80 and SOM.

Moose development is still in the early stages, but since it is really just an interface to the underlying meta-objects of Class::MOP it is pretty stable. In fact, stability and lack of deep/dark magic are two key design goals of both Moose and Class::MOP, as well as the idea that you only pay for the features you use, and nothing else. Because Moose is built upon a metaclass framework, it is possible to encode a lot of information in a very small amout of code, and let the metaclasses do the bulk of the work. Here is an example taken from the tests:

package BankAccount;
use strict;
use warnings;
use Moose;

has 'balance' => (isa => 'Int', is => 'rw', default => 0);

sub deposit {
    my ($self, $amount) = @_;
    $self->balance($self->balance + $amount);
}

sub withdraw {
    my ($self, $amount) = @_;
    my $current_balance = $self->balance();
    ($current_balance >= $amount)
        || confess "Account overdrawn";
    $self->balance($current_balance - $amount);
}

package CheckingAccount;
use strict;
use warnings;
use Moose;

extends 'BankAccount';

has 'overdraft_account' => (isa => 'BankAccount', is => 'rw');

before 'withdraw' => sub {
    my ($self, $amount) = @_;
    my $overdraft_amount = $amount - $self->balance();
    if ($overdraft_amount > 0) {
        $self->overdraft_account->withdraw($overdraft_amount);
        $self->deposit($overdraft_amount);
    }
};

The has keywords create instance attributes, with type constraints (isa => 'Int' etc), read/write accessors (is => 'rw') and "before" method wrappers allow for AOP style method "advice" which runs before the superclass method is called. But there is just as much that you don't see, such as; automatic inheritance from Moose::Object (where you inherit new and the Perl 6 style BUILD/BUILDALL constructor semantics), and full class introspection and reflection capabilities through the meta method.

Now, there are also a number of things which Moose is not. It is not another Inside-out object builder, it currently only supports blessed HASH based objects (surely the most common style), and possible future plans include adding support for blessed ARRAYs and such. But to be honest, one of the key ideas of Moose is to take away the need to worry about those types of things, and have everything Just Work, so that writing OO Perl code can be just as enjoyable as writing other Perl code.

Anyway, enough evangalism for now, please give Moose a try, I welcome any and all feedback (positive and negative), and feel free to come see us on #moose over at irc.perl.org.

-- Stevan

Thursday February 02, 2006
05:08 PM

Metaclasses in Perl 5

I have just released Class::MOP-0.01 to CPAN (appearing soon on a CPAN mirror near you). It attempts to create a complete meta-object protocol for Perl 5 and bring support for metaclass programming to Perl 5. Or course, this being a 0.01 release, there is still much that can be done and many improvements that can be made, but I feel it is a good start.

This is largely inspired by my work on the Perl 6 metamodel and my recent studies of CLOS, and as a side benefit, it is useful for my $work as well :)

Any questions, comments or criticisms are welcome.

- Stevan

Wednesday December 14, 2005
08:24 PM

Perl6::ObjectSpace in Haskell

Audrey recently began the process of porting the Perl6::ObjectSpace to Haskell to eventually become the new Pugs runcore. After creating the core (native) types in Haskell, audrey promptly implemented a mini-language using these types. It looks (of course) a lot like a very limited subset of Perl 6, but since we lambdacamels like our purity, it is side-effect free. Here is an example of a factorial function which uses a fixed point combinator to get around the fact we cannot call functions by name.

(-> $n {
    (-> &fact {
        &fact.(&fact, $n)
    }).(-> &f, $x {
            $x.eq(0).cond(
                -> { 1 },
                -> { $x.multiply( &f.(&f, $x.subtract(1)) ) }
            )
       });
}).(10);

The real purpose of this mini-language is to script the core runtime types to bootstrap the Perl 6 MetaModel. Today I commited the skeleton of the MetaModel Bootstrap PIL to Pugs. These are the first steps towards Pugs 6.28.0 and a fully realized Perl 6 object model in pugs.

- Stevan

Thursday November 24, 2005
11:35 AM

DBIx::Class meets Class::C3 meets GraphViz

mst has been busy converting his shiney new ORM masterpeice DBIx::Class to use Class::C3 instead of NEXT, and I have been helping out by dutifully applying all his patches to Class::C3. After some discussion yesterday about a weird bug, I decided that Class::C3 really needed a visualization tool to help in analyzing deep mulitple inheritance hierarchies. I thought about it for a while, then sat down and whipped this out in about 20 minutes to output in DOT (one of the Graphviz mini-languages). It is still rather primative, but it pretty much does what I wanted it to. Here are some examples I did to show mst:

NOTE: The green lines show the @ISA relationships, and the red lines show the C3 method dispatch order.

Wednesday October 19, 2005
04:53 PM

Autogenerated Perl6::MetaModel diagrams

I have been pondering createing some kind of class-viewer of sorts for the Perl6::MetaModel prototype. For two reasons; first, it would be highly useful for my own debugging, and secondly because it would be really cool :)

Today I sat down and finally did it. It is currently an ugly kludge to print out GraphViz dot files, but it does the job. I have chosen some of the more interesting outputs from some of the test files, and uploaded them here.

-- Stevan

Saturday October 08, 2005
09:45 PM

Method Dispatch with Eigenclasses

This past week I have been busy with $work, however, I have been thinking quite heavily about how Eigenclasses can fit into the Perl6-MetaModel. Currently the implementation has limited support (actually it's pretty much broken support) for eigenclasses, and the past 3-4 attempts I have made to clean things up have resulted in a lot of svk revert commands. So finally I decided to sit down and draw things out.

The first diagram I made was your basic vanilla method dispatch. It shows both instance method dispatch as well as class method dispatch. This diagram illustrates the key issue with class methods, in that they do not fit cleanly into a meta-model. This can also be seen when you look at the method signature of a class method:

class Bar {
    method foo (Class $c:) { ... }
}

In any other method, the invocant (shown as $c here) would be of the same type as the class in which it is defined. But here the signature of the class method shows that the invocant should be of type Class, however that is not where the method is defined.

This issue is even more apparent in CLOS, where methods are represented with generic functions (which are a lot like multi-methods). In CLOS the first parameter of a method has what is called a specializer (which is basically the "type" of that expected parameter) that parameter is used by the CLOS system to associate a method with a particular class. Here is a short example of a generic function foo which is associated with the class Bar.

(defgeneric foo (self))
(defmethod foo ((Bar self) ( ... ))

The first line defines the generic function foo with the single parameter self. The second line defines a method of that generic function, specialized for the Bar class. Now when we create a class-method called baz, it will look like this:

(defmethod baz ((standard-class c)) (...))

By making the type of our parameter standard-class CLOS will associate this method with all instances of standard-class, which is every class in the system.

So you can see, it is impossible to specialize a class method to one particular class using these meta-models.

Eigenclasses to the rescue!!

My second diagram shows how class-methods can be implemented using ruby-style eigenclasses (or singleton-methods as they are also called). The eigenclass hierarchy (show here with the e- prefix) essentially mirrors the normal class hierarchy, but instead of storing instance methods, they store class methods. (This parallel hierarchy actually reminds me of the Smalltalk metaclass hierarchy in some ways, which is why I think it "smells" right to me).

Using the eigenclasses results in a very normalized method dispatch mechanism where instance method and class method dispatch are exactly the same. What else can I say about that but ruby++.

The only point at which things get messy is when we introduce an intentional subclass of Class. Like this:

class Baz is Class { ... }
class Foo meta Baz { ... }
class Bar is Foo { ... }

The result is that e-Foo inherits from Baz, and since e-Bar inherits from e-Foo, e-Bar gets methods from Baz, which is probably now what was intended. I didn't even bother to draw this out since it gets really really messy. However, it occurred to me that using Role composition to inject class methods, it might be possible to accomplish most anything you would want to accomplish through subclassing Class anyway. However, this needs more contemplation, and so I will save it for another day.

- Stevan

Monday August 29, 2005
07:39 AM

Perl6::MetaModel 2.0

My $work has been keeping me busy lately, and so my Perl 6/pugs work has been suffering. However, due to a $work induced eratic sleep schedule I found myself up very early this morning with nothing to do, so I finished/fixed the bootstrapping phase of the Perl6-MetaModel 2.0.

This new version attempts to build upon the things that worked in 1.0, while reducing the dependence on Perl 5 magic in the core model. It also eliminates the "meta" layer (at least in name) in favor of a more direct "Class" and "Object" layers. The result is a much simplier implementation which I hope can be ported to the other runtimes even easier than 1.0. I have also adopted autrijus's gnostic naming conventions for the core files.

- Stevan

Monday August 08, 2005
12:33 AM

Perl 6, Class::Std and the C3 method dispatch

So all this work on the Perl 6 metamodel has made me really, really, really want some of the Perl 6 OO features in Perl 5. In particular things like opaque instances, inherited attributes and a sane method resolution order under multiple inheritance (C3).

Autrijus recently recomended I give Class::Std a try, since it provides many Perl6-ish features. And this morning, I did just that, and I was quite happy to find not only opaque instances (using inside-out objects), but inherited attributes as well as inherited destructors and multiply dispatching methods.

Now, being someone who actually likes to read code, I decided to go source-diving. And this being a Damian module, I was prepared for plenty of the insane code which Damian is known for. But much to my suprise, I found the code quite readable and very straightfoward.

I was happy to see that the BUILD and DESTROY orders are not the standard Perl 5 depth-first/left-to-right orderings. But instead use a custom ordering which seems to produce a fairly sane ordering. And it also seemed that the multple dispatch mechanisms and AUTOMETH feature used the same order as well.

However, I did not like the fact that these orderings stood in contrast with the way methods were actually dispatched. The standard Perl 5 method dispatch was still used for standard method dispatch. So after some head scratching and a little experimentation, I came up with Class::C3.

While still highly experimental, Class::C3 is quickly evolving, and version 0.02 should available at a CPAN mirror near you very soon. It basically pre-caches all the method dispatch to insure that the C3 method resolution ordering will be used. Version 0.02 also supports re-initializing the dispatch cache as well for those crazy people who like to mess with @ISA during runtime. I have yet to actually test it with Class::Std yet, but I suspect the two modules will get along well.

- Stevan

Saturday August 06, 2005
01:04 AM

10,000 ft View of Perl 6 MetaModel

Earlier today on #perl6, kolibrie asked me about documentation for the metamodel. Sadly, none actually existed, and with all the bootstapping code in the Perl 5 version, just "reading the source" can be confusing. So while I was waiting for some large data files for $work to upload, I sketched out a quick 10,000 ft view of the metamodel.

    i(Foo)     - instance of Foo
    Foo        - the user-level Foo class
    class(Foo) - the instance of Perl6::Class named "Foo"
    meta(Foo)  - the instance of Perl6::MetaClass which describes Foo

                +---------------+
                | i(Foo)        |
                +---------------+
                | class         |------+
                | instance_data |      |
                +---------------+      |
                                       |
                                       |   NOTE:
                      ___              |   At the Foo level is where
                     (   )             |   the interpreter will likely
      +-------------( Foo )------------+   perform some 'magic' to
      |              (___)                 connect the user and meta
      |                                    levels.
      |           <user level>
  ....|.........................................................
      |           <meta level>
      |
      |         +---------------+
      +-------->| class(Foo)    |
                +---------------+
      +---------| meta          |
      |         | name          |
      |         +---------------+
      |
      |         +---------------+
      +-------->| meta(Foo)     |
                +---------------+
                | name          |
                | version       |
                | authority     |
                | %methods      |
                | %attributes   |
                +---------------+

Any comments, questions, suggestions or even disparaging remarks are welcome. A more complete document is in the pugs tree here.

- Stevan