What’s Next for Object-Oriented Perl?
Tags: perl, oop, corinna, moo
Published: 2021-08-03 15:22:03

---
title: What’s Next for Object-Oriented Perl?
published: true
date: 2021-08-03 14:00:00 UTC
tags: Perl,OOP,Corinna,Moo
canonical_url: https://phoenixtrap.com/2021/08/03/whats-next-oo-perl/
cover_image: https://dev-to-uploads.s3.amazonaws.com/uploads/articles/0lh96gr3zytcap9kqfjp.jpeg
---

## Introduction: The current state of play

Perl has “very minimal” support for object-oriented (OO) programming out of the box [by its own admission](https://perldoc.perl.org/perlootut#DESCRIPTION). It’s class-based but classes are [just packages](https://perldoc.perl.org/perlobj#A-Class-is-Simply-a-Package) used differently. Objects are [just data structures](https://perldoc.perl.org/perlobj#An-Object-is-Simply-a-Data-Structure) [`bless`](https://perldoc.perl.org/functions/bless)ed into a class, methods are [just subroutines](https://perldoc.perl.org/perlobj#A-Method-is-Simply-a-Subroutine) whose first argument is an object or class name, and [attributes/properties are often just the key-value pair of a hash](https://perldoc.perl.org/perlobj#Attributes) 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](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Working_with_Objects#objects_and_properties).) You’ve got [polymorphism](https://perldoc.perl.org/perlootut#Polymorphism), [inheritance](https://perldoc.perl.org/perlobj#Inheritance), and it’s up to you to enforce [encapsulation](https://perldoc.perl.org/perlootut#Encapsulation).

This can take a lot of work to use effectively. To help address that, [several systems](https://perldoc.perl.org/perlootut#PERL-OO-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](https://metacpan.org/pod/Moo): it’s got the features I need 90% of the time like [built-in constructors](https://metacpan.org/pod/Moo#new), [roles](https://metacpan.org/pod/Moo#with) (an alternative to composition through inheritance), [attributes](https://metacpan.org/pod/Moo#has), [type validation](https://metacpan.org/pod/Moo#isa), and [method modifiers](https://metacpan.org/pod/Class::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](https://metacpan.org/pod/Moose) and its [meta-object protocol](https://metacpan.org/pod/Class::MOP) with minimal effort.

## Corinna, Object::Pad, and porting dbcritic

But there’s a new kid on the block. [Curtis “Ovid” Poe](https://ovid.github.io/) has been spearheading [Corinna](https://github.com/Ovid/Cor/wiki), an effort “to bring effective OO to the Perl core and _leapfrog_ [emphasis his] the capabilities of many OO languages today.” No [CPAN](https://www.cpan.org/) 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](https://leonerds-code.blogspot.com/) _(maybe I should get a cool nickname too?)_ has been implementing some of these ideas as [new Perl keyword syntax](https://perldoc.perl.org/perlapi#PL_keyword_plugin) in his [Object::Pad](https://metacpan.org/pod/Object::Pad) module.

Both [Ovid](https://twitter.com/OvidPerl/status/1420323829356843011) and [LeoNerd have been asking developers to try out Object::Pad](https://twitter.com/cpan_pevans/status/1415596477968261121), 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](http://mjgardner.github.io/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](https://github.com/mjgardner/dbcritic/tree/object_pad).

First, the marquee result: the [code for App::DBCritic](https://github.com/mjgardner/dbcritic/blob/object_pad/lib/App/DBCritic.pm) (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](https://metacpan.org/pod/Object::Pad#has) (called “slots” in its documentation) and lack of explicit support for Moo’s [attribute coercion](https://metacpan.org/pod/Moo#coerce). 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`](https://metacpan.org/pod/Object::Pad#BUILD) or [`ADJUST`](https://metacpan.org/pod/Object::Pad#BUILD) phase blocks for now.

Before, a Moo attribute with various options:

```perl
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:

```perl
has $schema :reader :param = undef;
```

Speaking of `ADJUST` blocks, it took a little bit of insight from the [#perl IRC channel](https://web.libera.chat/#perl) 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`](https://metacpan.org/pod/Moo#lazy) attributes and [`builder`](https://metacpan.org/pod/Moo#builder) methods. Clarifying the main set of optional constructor arguments into a [single `ADJUST` block](https://github.com/mjgardner/dbcritic/blob/object_pad/lib/App/DBCritic.pm#L82) 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:

```perl
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](https://perldoc.perl.org/perldata#Scalar-values) and [references](https://perldoc.perl.org/perlref) contained in scalars. This means methods in your class can eliminate a [dereferencing](https://perldoc.perl.org/perlref#Using-References) step, again leading to cleaner code. I used this specifically in the [`@violations` array](https://github.com/mjgardner/dbcritic/blob/object_pad/lib/App/DBCritic.pm#L124) and [`%elements` hash](https://github.com/mjgardner/dbcritic/blob/object_pad/lib/App/DBCritic.pm#L114) slots and was very pleased with the results.

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

```perl
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](https://metacpan.org/pod/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](https://metacpan.org/pod/Dist::Zilla::Plugin::Test::UnusedVars)-generated [Test::Vars](https://metacpan.org/pod/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](https://metacpan.org/pod/Test::Vars#all_vars_ok(%args)), though, so I can explore using those and possibly file a [pull request](https://github.com/houseabsolute/p5-Test-Vars/pulls) 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](https://qa.perl.org/phalanx/kwalitee.html) 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.