Look, I get it. You don’t like the Perl programming language or have otherwise disregarded it as dead.” (Or perhaps you haven’t, in which case please check out my other blog posts!) It has weird noisy syntax, mixing regular expressions, sigils on variable names, various braces and brackets for data structures, and a menagerie of cryptic special variables. It’s old: 34 years in December, with a history of (sometimes amateur) developers that have used and abused that syntax to ship code of questionable quality. Maybe you grudgingly accept its utility but think it should die gracefully, maintained only to run legacy applications.

But you know what? Perl’s still going. It’s had a steady cadence of yearly releases for the past decade, introducing new features and fencing in bad behavior while maintaining an admirable level of backward compatibility. Yes, there was a too-​long adventure developing what started as Perl 6, but that language now has its own identity as Raku and even has facilities for mixing Perl with its native code or vice versa.

And then there’s CPAN, the Comprehensive Perl Archive Network: a continually-​updated collection of over 200,000 open-​source modules written by over 14,000 authors, the best of which are well-​tested and ‑documented (applying peer pressure to those that fall short), presented through a search engine and front-​end built by scores of contributors. Through CPAN you can find distributions for things like:

All of this is available through a mature installation toolchain that doesn’t break from month to month.

Finally and most importantly, there’s the global Perl community. The COVID-​19 pandemic has put a damper on the hundreds of global Perl Mongers groups’ meetups, but that hasn’t stopped the yearly Perl and Raku Conference from meeting virtually. (In the past there have also been yearly European and Asian conferences, occasional forays into South America and Russia, as well as hackathons and workshops worldwide.) There are IRC servers and channels for chat, mailing lists galore, blogs (yes, apart from this one), and a quirky social network that predates Facebook and Twitter.

So no, Perl isn’t dead or even dying, but if you don’t like it and favor something newer, that’s OK! Technologies can coexist on their own merits and advocates of one don’t have to beat down their contemporaries to be successful. Perl happens to be battle-​tested (to borrow a term from my friend Curtis Ovid” Poe), it runs large parts of the Web (speaking from direct and ongoing experience in the hosting business here), and it’s still evolving to meet the needs of its users.

Introduction: The current state of play

Perl has very minimal” support for object-​oriented (OO) programming out of the box by its own admission. It’s class-​based but classes are just packages used differently. Objects are just data structures blessed into a class, methods are just subroutines whose first argument 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 feature shared with JavaScript, whose prototype-​based objects are just collections of key-​value pairs with the keys addressed as properties.) You’ve got polymorphism, inheritance, and it’s up to you to enforce encapsulation.

This can take a lot of work to use effectively. To help address that, several systems have been developed over the years to reduce boilerplate and provide modern (or postmodern”) OO features that developers from other languages expect. My favorite for a while has been Moo: it’s got the features I need 90% of the time like built-​in constructors, roles (an alternative to composition through inheritance), attributes, type validation, and method modifiers for enhanced polymorphism. And if I need to dig around in the guts of classes, attributes, and the like I can always upgrade to Moo’s big brother Moose and its meta-​object protocol with minimal effort.

Corinna, Object::Pad, and porting dbcritic

But there’s a new kid on the block. Curtis Ovid” Poe has been spearheading Corinna, an effort to bring effective OO to the Perl core and leapfrog [emphasis his] the capabilities of many OO languages today.” No CPAN modules, no chain of dependencies; just solid OO features and syntax built-​in. And while Corinna is a ways off from shipping, Paul LeoNerd” Evans (maybe I should get a cool nickname too?) has been implementing some of these ideas as new Perl keyword syntax in his Object::Pad module.

Both Ovid and LeoNerd have been asking developers to try out Object::Pad, not just as a new toy, but to get feedback on what works and what needs to be added. So I thought I’d try porting an older small Moo-​based project named dbcritic to this new reality. In the process, I learned some of the advantages and disadvantages of working with Object::Pad. Hopefully, this can inform both it and Corinna’s evolution as well as other curious developers’ evaluations. You can follow my coding efforts in this GitHub branch.

First, the marquee result: the code for App::DBCritic (the class I started with) is cleaner and shorter, with 33 lines shaved off so far. Mainly this is due to Object::Pad’s more concise attribute syntax (called slots” in its documentation) and lack of explicit support for Moo’s attribute coercion. I only used the latter for one attribute in the Moo version and I’m not sure it worked particularly well, so it wasn’t hard to jettison. But if your code supports coercions extensively, you’ll have to look into Object::Pad’s BUILD or ADJUST phase blocks for now.

Before, a Moo attribute with various options:

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

After, an Object::Pad slot. No coercion and builder code is handled in a later ADJUST block:

has $schema :reader :param = undef;

Speaking of ADJUST blocks, it took a little bit of insight from the #perl IRC channel to realize that they were the appropriate place for setting slot defaults that are computed from other slots. Previously I was using a maze of dependencies mixing Moo lazy attributes and builder methods. Clarifying the main set of optional constructor arguments into a single ADJUST block helped untangle things, so this might be an indication that lazy attributes are an antipattern when trying to write clean code. It’s also worth noting that Object::Pad ADJUST blocks run on object construction, whereas Moo lazy attributes are only built when needed. This tends to matter for database 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 advantage over Moo and Moose attributes: they directly support Perl array and hash data structures, while the latter only supports scalars and references contained in scalars. This means methods in your class can eliminate a dereferencing step, again leading to cleaner code. I used this specifically 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 development lifecycle issues with Object::Pad, but they’re mainly a result of its future-​facing syntax. I had to give up using perltidy and perlcritic in my build and test phases, respectively: perltidy doesn’t understand slot attributes like :reader and :param and will emit an error file (but code still compiles), and several of the perlcritic policies I use report problems because its PPI parser doesn’t recognize the new syntax. I could add exceptions in the perlcriticrc file and litter my code with more ## no critic annotations than it already had, but at this point, it was easier to just disable it entirely.

Another thing I had to disable for now was my Dist::Zilla::Plugin::Test::UnusedVars-generated Test::Vars test for detecting unused variables, as it reports multiple failures for the hidden @(Object::Pad/slots) variable. It does have options for ignoring certain variables, though, so I can explore using those and possibly file a pull request to ignore that variable by default.

Conclusion: The future looks bright

Overall I’m satisfied with Object::Pad and by extension some of the syntax that Corinna will introduce. I’m going to try porting the rest of dbcritic and see if I can work around the issues I listed above without giving up the kwalitee improvement tools I’m used to. I’ll post my findings if I feel it merits another blog.

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

This week we considered a viewer’s pull request, added administrator login, and started on adding the SQLite database that will store the administrator’s acceptance of assignments. We also shored up file upload permissions for authenticated users only and added a logout link, learning about some more Mojolicious helpers.

You can find the whole series here.