July 2013 Archives

More tests and more traits for p5-MOP

I have been spending a lot of time lately porting modules to p5-mop as a way to really stretch and test out the prototype. Additionally a few other people are also porting modules as well. The result is that we now have a nicely expanding "external" test suite. This is something I found with Moose, while it is very nice to have a good size test suite, it is even better to have real modules (that perform real work) and themselves have good test suites. For a module like Moose and a project like p5-mop, this kind of testing is critical in exposing issues that normal unit tests just won't shake out.

Modules ported to p5-mop

I recently started porting Plack to the p5-mop and yesterday I completed the "straight" port of the code. This means I didn't try to refactor anything to take advantage of any p5-mop features, I just converted plain Perl classes to their p5-mop equivalent.

My main reasoning for doing this was that I really wanted to push this prototype in real world scenarios, not just contrived tests and simplistic examples. This is a mistake I believe we made with the first prototype. So that said, I am happy to report that all 1152 of the Plack tests are passing.

Traits, traits and more traits

So, a few days ago I posted about adding p6-style traits to the p5-mop, a couple days later I posted about how they helped me keep things simple when adding overload support to the p5-mop. Since this is a new approach and has not had the benefit of battle testing that the subclass/role heavy Moose approach has, I have been experimenting with it to see how far it can be taken.

Keeping It Simple (... and not being stupid)

I have never been a fan of Perl's operator overloading support, not only is the API kind of awkward, but the functionality is limited and fairly inflexible (at least compared to other languages like OCaml and Scala). But that said, I felt it was important to have some support for it in the new p5-mop.

p5-MOP gets p6 style traits (sorta)

So the other day in #p5-mop we were discussing how to handle meta layer extensions. For example, doing things like adding accessor generation support to attributes.

class Foo {
    has $foo ( is => 'ro' );

The traditional approach found in the old p5-mop and in Moose was to do this by subclassing things in the metaclass or applying roles to the existing metaobjects (as is the favored approach with Moose). But to be honest, this approach is kind of tedious and often ends up requiring a lot of subclassing and mucking about.

So, in the grand tradition of stealing cool stuff from Perl 6 for use in Perl 5, we did just that (kinda sorta).

p5-MOP syntax improvements

I spent much of the last few days working on some syntactic improvements to the p5-mop. I had originally went with a fairly straightforward method for specifying things like inheritance, role composition, etc. It basically looks like this:

class Foo (extends => 'Bar', with => ['Baz', 'Gorch']) {}

This works well because it is basically just using a simple Perl list to pass in information to the underlying meta-objects. My only issue with this is that everything is a string, which is no different then what we have now using base or @ISA, but I wanted to see if I could improve this a little.

After wrestling with Devel::Declare for a while (yes, I know it is evil, but it is just for the prototype, the real version will not use it), I was able to get the following syntax to work.

class Foo extends Bar with Baz, Gorch {}

I like this because in general I feel it is cleaner and as a bonus saves a bunch of typing of quotes and other symbols (read: reduces "line noise"). The best part about this is that it really just desugars down to the old form, meaning I didn't have to add any complexity to the MOP, only to the syntax parser.

Currently this code resides in a branch as I want to do some more parser stress tests before I put it into master, but so far it is working quite well.

Roles in p5-MOP

Support for roles has now been added to the p5-mop-redux project. I ported the example that I used a few times in talks and in previous p5-mop attempts. I like this example because it shows a number of features and styles of roles, such as; required methods, roles composing into roles, interface roles and finally multiple roles composing into a class.

Over the years I have written a number of implementations of roles, it started with Class::Trait back in 2004, followed by a couple of attempts in the Pugs project, then came Moose::Role and most recently in the old p5-mop project - and I never really liked how any of them turned out. The implementations always felt overly complex and never seemed to gel in my head completely. Since I am really striving to keep things simple with this version of the MOP I decided to avoid the crazy bootstrapping gymnastics and start with the simplest hack possible (then clean it up in the bootstrap).

And I am happy to say, it worked out! ... and as a bonus I accidentally created a new feature; abstract classes. Abstract classes are simply a class with a required method, they will compile, but will fail if you try to instantiate them. This also created a change in how roles and classes work, composing a role with a required method into a class, and not fulfilling that requirement will no longer break at compile time, but instead will create an abstract class. I am unsure if this is actually a good thing or not, but I want to experiment more with abstract classes before I pass judgement.

Next goal is to refactor some of the guts of attributes so that it is both more correct (ultimately meaning more extensible) and faster (basically making things lazier).

On the relationship between p5-MOP and Moe

I was asked on twitter recently what impact my restarting the p5-MOP project will have on Moe. My response was basically that my time would be split between the two projects, so momentum would probably slow. But that's actually not true, my time right now is completely focused on p5-MOP, and Moe is kind of on the back burner (for me at least, the other contributors need not wait for me).

But this question got me thinking, specifically thinking about the relationship between these two projects.

I realize now that when I started the p5-MOP project I was lacking some very crucial knowledge and experience. I had spent the last 7 years just building features on top of a language, and really had no experience building them inside of a language. I can see now that my approach reflected that lack of knowledge and experience, and that it ultimately lead to the issues that suffocated that project.

By starting completely fresh with Moe I believe I was able to gain some insight into how one goes about constructing a language from the inside out. I realize of course that hacking a bunch of Scala code is radically different then hacking the C code that is found in the perl core, but for me it was more about the change of perspective then anything else. And this new perspective is what has driven my new approach to the p5-MOP. So I guess you could say I never really stopped working on p5-MOP, just took a rather long and indirect route.

BTW, I do plan to blog more about the details of this new approach, but for now I need to work on getting Roles into the prototype (starting first by reviewing the work Toby Inkster did this weekend).

About Stevan Little

user-pic I am the original author of Moose and am (again) working on the p5-mop project to bring some of the core features of Moose into the core. I am also conducting a thought experiment called Moe to see what an Ultra Modern Perl 5 would look like.