What’s Next for Object-​Oriented Perl?

Introduction: The current state of play

Perl has ​“very min­i­mal” sup­port for object-​oriented (OO) pro­gram­ming out of the box by its own admis­sion. It’s class-​based but class­es are just pack­ages used dif­fer­ent­ly. Objects are just data struc­tures blessed into a class, meth­ods are just sub­rou­tines whose first argu­ment is an object or class name, and attributes/​properties are often just the key-​value pair of a hash stored in the object. (This last is a fea­ture shared with JavaScript, whose prototype-​based objects are just col­lec­tions of key-​value pairs with the keys addressed as prop­er­ties.) You’ve got poly­mor­phism, inher­i­tance, and it’s up to you to enforce encap­su­la­tion.

This can take a lot of work to use effec­tive­ly. To help address that, sev­er­al sys­tems have been devel­oped over the years to reduce boil­er­plate and pro­vide mod­ern (or ​“post­mod­ern”) OO fea­tures that devel­op­ers from oth­er lan­guages expect. My favorite for a while has been Moo: it’s got the fea­tures I need 90% of the time like built-​in con­struc­tors, roles (an alter­na­tive to com­po­si­tion through inher­i­tance), attrib­ut­es, type val­i­da­tion, and method mod­i­fiers for enhanced poly­mor­phism. And if I need to dig around in the guts of class­es, attrib­ut­es, and the like I can always upgrade to Moo’s big broth­er Moose and its meta-​object pro­to­col with min­i­mal effort.

Corinna, Object::Pad, and porting dbcritic

But there’s a new kid on the block. Curtis ​“Ovid” Poe has been spear­head­ing Corinna, an effort ​“to bring effec­tive OO to the Perl core and leapfrog [empha­sis his] the capa­bil­i­ties of many OO lan­guages today.” No CPAN mod­ules, no chain of depen­den­cies; just sol­id OO fea­tures and syn­tax built-​in. And while Corinna is a ways off from ship­ping, Paul ​“LeoNerd” Evans (maybe I should get a cool nick­name too?) has been imple­ment­ing some of these ideas as new Perl key­word syn­tax in his Object::Pad module.

Both Ovid and LeoNerd have been ask­ing devel­op­ers to try out Object::Pad, not just as a new toy, but to get feed­back on what works and what needs to be added. So I thought I’d try port­ing an old­er small Moo-​based project named dbcrit­ic to this new real­i­ty. In the process, I learned some of the advan­tages and dis­ad­van­tages of work­ing with Object::Pad. Hopefully, this can inform both it and Corinna’s evo­lu­tion as well as oth­er curi­ous devel­op­ers’ eval­u­a­tions. You can fol­low my cod­ing efforts in this GitHub branch.

First, the mar­quee result: the code for App::DBCritic (the class I start­ed with) is clean­er and short­er, with 33 lines shaved off so far. Mainly this is due to Object::Pad’s more con­cise attribute syn­tax (called ​“slots” in its doc­u­men­ta­tion) and lack of explic­it sup­port for Moo’s attribute coer­cion. I only used the lat­ter for one attribute in the Moo ver­sion and I’m not sure it worked par­tic­u­lar­ly well, so it was­n’t hard to jet­ti­son. But if your code sup­ports coer­cions exten­sive­ly, you’ll have to look into Object::Pad’s BUILD or ADJUST phase blocks for now.

Before, a Moo attribute with var­i­ous options:

has schema => (
    is        => 'ro',
    coerce    => 1,
    lazy      => 1,
    default   => \&_build_schema,
    coerce    => \&_coerce_schema,
    predicate => 1,
);

After, an Object::Pad slot. No coer­cion and builder code is han­dled in a lat­er ADJUST block:

has $schema :reader :param = undef;

Speaking of ADJUST blocks, it took a lit­tle bit of insight from the #perl IRC chan­nel to real­ize that they were the appro­pri­ate place for set­ting slot defaults that are com­put­ed from oth­er slots. Previously I was using a maze of depen­den­cies mix­ing Moo lazy attrib­ut­es and builder meth­ods. Clarifying the main set of option­al con­struc­tor argu­ments into a sin­gle ADJUST block helped untan­gle things, so this might be an indi­ca­tion that lazy attrib­ut­es are an antipat­tern when try­ing to write clean code. It’s also worth not­ing that Object::Pad ADJUST blocks run on object con­struc­tion, where­as Moo lazy attrib­ut­es are only built when need­ed. This tends to mat­ter for data­base access.

The ADJUST block for the $schema slot:

ADJUST {
    my @connect_info = ( $dsn, $username, $password );
    if ($class_name and eval "require $class_name") {
        $schema = $class_name->connect(@connect_info);
    }
    elsif ( not ( blessed($schema) and $schema->isa('DBIx::Class::Schema') ) ) {
        local $SIG{__WARN__} = sub {
            if ( $_[0] !~ / has no primary key at /ms ) {
                print {*STDERR} $_[0];
            }
        };
        $schema = App::DBCritic::Loader->connect(@connect_info);
    }
    croak 'No schema defined' if not $schema;
}

Object::Pad’s slots have one great advan­tage over Moo and Moose attrib­ut­es: they direct­ly sup­port Perl array and hash data struc­tures, while the lat­ter only sup­ports scalars and ref­er­ences con­tained in scalars. This means meth­ods in your class can elim­i­nate a deref­er­enc­ing step, again lead­ing to clean­er code. I used this specif­i­cal­ly in the @violations array and %elements hash slots and was very pleased with the results.

The @violations and %elements slots and their ADJUST blocks:

has %elements;

ADJUST {
    %elements = (
        Schema       => [$schema],
        ResultSource => [ map { $schema->source($_) } $schema->sources ],
        ResultSet    => [ map { $schema->resultset($_) } $schema->sources ],
    );
}

has @violations;

ADJUST {
    @violations = map { $self->_policy_loop( $_, $elements{$_} ) }
        keys %elements;
}

method violations { wantarray ? @violations : \@violations }

Issues

I did have some devel­op­ment life­cy­cle issues with Object::Pad, but they’re main­ly a result of its future-​facing syn­tax. I had to give up using perltidy and perlcritic in my build and test phas­es, respec­tive­ly: perltidy does­n’t under­stand slot attrib­ut­es like :reader and :param and will emit an error file (but code still com­piles), and sev­er­al of the perlcritic poli­cies I use report prob­lems because its PPI pars­er does­n’t rec­og­nize the new syn­tax. I could add excep­tions in the perlcriticrc file and lit­ter my code with more ## no critic anno­ta­tions than it already had, but at this point, it was eas­i­er to just dis­able it entirely.

Another thing I had to dis­able for now was my Dist::Zilla::Plugin::Test::UnusedVars-gen­er­at­ed Test::Vars test for detect­ing unused vari­ables, as it reports mul­ti­ple fail­ures for the hid­den @(Object::Pad/slots) vari­able. It does have options for ignor­ing cer­tain vari­ables, though, so I can explore using those and pos­si­bly file a pull request to ignore that vari­able by default.

Conclusion: The future looks bright

Overall I’m sat­is­fied with Object::Pad and by exten­sion some of the syn­tax that Corinna will intro­duce. I’m going to try port­ing the rest of dbcrit­ic and see if I can work around the issues I list­ed above with­out giv­ing up the kwali­tee improve­ment tools I’m used to. I’ll post my find­ings if I feel it mer­its anoth­er blog.

What do you think? Is this the future of object-​oriented Perl? Let me know in the com­ments below.


Discover more from The Phoenix Trap

Subscribe to get the latest posts sent to your email.

Mark Gardner Avatar

Hi, I’m Mark.

Hi, I’m Mark Gard­ner, and this is my personal blog. I show software developers how to level up by building production-ready things that work. Clear code, real projects, lessons learned.

Comments

14 responses to “What’s Next for Object-​Oriented Perl?”

  1. sigzero Avatar

    Is it the future? I hope so. Perl needs it and I encour­aged by Ovid and LeoNerd as the imple­men­tors to get it done.

  2. […] What’s Next for Object-​Oriented Perl? […]

  3. brianwisti Avatar

    I like it. My main chal­lenge is same as the first time I saw Moose: looked like a vast improve­ment over what came before, but I did­n’t have a spe­cif­ic project in mind for it. My sec­ond chal­lenge is the syn­tax is so much more pleas­ant inside an Object::Pad class block that I’d be tempt­ed to make every­thing an object.

    1. Mark Gardner Avatar

      I think the most bril­liant thing is slots as lex­i­cal vari­ables. It makes them absolute­ly pri­vate by default unless you add a read­er and/​or writer and/​or muta­tor. Much bet­ter than Moose or Moo’s way of doing every­thing through the acces­sor and using a nam­ing con­ven­tion to mere­ly sug­gest that an attribute might be pri­vate. And I’ve seen that lat­ter bit vio­lat­ed all over in my code­base at work.

  4. Michael Labrecque Avatar
    Michael Labrecque

    How is it on mem­o­ry and speed com­pared to moo and blessed objects?

    1. Mark Gardner Avatar

      Not sure about mem­o­ry, but for speed check out Bencher::Scenarios::Accessors for some bench­mark sce­nar­ios of var­i­ous object sys­tems, includ­ing Moo, Moose, and plain blessed objects.

  5. Ed S. Avatar
    Ed S.

    I have to say that I’m not enam­ored with the Corinna/Object::Pad syn­tax. It feels like things were changed for no good rea­son. A syn­tax clos­er to Moo or Moose would have been nice in order to ease the tran­si­tion. Just my two cents.

    1. Mark Gardner Avatar

      I don’t think there’s nec­es­sar­i­ly a ​“tran­si­tion.” Moo or Moose won’t sud­den­ly break. Corinna will just be a good base­line set of tools for projects going forward.

      1. Ed S. Avatar
        Ed S.

        Well, I cer­tain­ly would look to tran­si­tion my mod­ules from Moo to what­ev­er the core OO imple­men­ta­tion is. I’m sure oth­ers will as well. There are too many advan­tages to being dis­trib­uted with Perl. I just wish the syn­tax was clos­er to Moo’s or just go with a Moo 2.0 instead of start­ing over from scratch.

        1. Mark Gardner Avatar

          That’s fair. You’re not the only one to have expressed this sentiment.

  6. […] ← What’s Next for Object-​Oriented Perl? […]

  7. […] I want­ed to high­light a point I made in one of the com­ments last week: Object::Pad’s slots (a.k.a. fields, attrib­ut­es, what­ev­er) are pri­vate by default, completely […]

  8. […] I want­ed to high­light a point I made in one of the com­ments last week: Object::Pad’s slots (a.k.a. fields, attrib­ut­es, what­ev­er) are pri­vate by default, completely […]

  9. Bill Raty Avatar
    Bill Raty

    I think the future needs more buff­ing. The down­side of Perl’s cur­rent OO helpers is that they’re syn­tac­tic sug­ar on-​top of base Perl OO mech­a­nisms. Object::Pad, and the need for ADJUST blocks and scoped hash­es indi­cates sim­ple adjust­ment of the deck chairs on the Titanic while it is sink­ing. Perl remains, 21 years after we had our hopes up with Perl6, at a com­pet­i­tive dis­ad­van­tage against oth­er lan­guages. We don’t need anoth­er Moo or Moose. What we need is a way to define a class as eas­i­ly as Python or Ruby with­out hav­ing to deploy CPAN pack­ages, in a MVP form.

Your post must include a link to this post.

How does this work?
Write a response on your own website and include a link to this post. Then submit your response’s URL here. Your response may appear here after moderation. To update or remove it, edit or delete your original post and submit the same URL again. Learn more about Webmentions.