DEV Community

loading...
Cover image for Bringing Modern OO To Perl

Bringing Modern OO To Perl

ovid profile image Ovid ・4 min read

Years ago, when I first stumbled on Perl, I had been programming in many languages, including C, assembler, COBOL, BASIC, and Java. I had been working at an insurance company and replicated 80 lines of COBOL in only ten lines of Perl and jumped ship. Since then I've written books about the language, keynoted several conferences, and sit on the Perl Foundation Board of Directors.

Fast forward to today and I'm still working with Perl, though often as a consultant, and more on a management level, overseeing projects. But I like to keep my hand in and unfortunately, Perl's object-oriented programming (OOP) syntax is wanting. Our company, All Around the World is routinely contacted by clients wanting us to build new systems in Perl or to rescue legacy implementations and I brace myself for whatever variant of "OO programming" they happen to use. You see, Perl suffers from The Lisp Curse. It's just so easy to write your own OO system. I've done it myself. Heck, I even created (as a joke) one that used an inverted inheritance MRO similar to the BETA programming language.

Today, Perl's core OOP system relies on something called bless and is conceptually very similar to how Python implements OOP. But most developers today hit the CPAN and download one of the myriad OOP systems and lament that their personal favorite isn't part of the Perl core.

I decided to fix that. But what should be in the Perl core? While there are tons of different OOP systems out there, it's safe to say that Moose and Moo are the most popular choices. For reasons I won't belabor here, the Perl 5 Porters—the developers who maintain the language—have not accepted Moo/se into the core. But should they?

Stevan Little, the author of Moose, has been clear that Moose shouldn't be in the core and he was working on Moxie, something he felt was a much better choice. In fact, it is a much better choice, but only if you accept the current limitations of Perl's syntax. I didn't want to, so Corinna was born. This is nothing short of a radical attempt to overhaul Perl OOP in a way that is both backwards-compatible, but also allows the overall language to grow. And to say that the syntax is, um, nicer, is a massive understatement.

This, for example, is a simple LRU cache:

class Cache::LRU {
  use Hash::Ordered;

  has $max_size :new  :reader = 20;
  has $cache    :handles(get) = Hash::Ordered->new;
  has $created  :reader       = time;

  CONSTRUCT(%args) {
    if ( exists $args{max_size} && $args{max_size} < 1 ) {
      croak("max_size argument must not be less than 1");
    }
    return %args;
  }

  method set ( $key, $value ) {
    if ( $cache->exists($key) ) {
      $cache->delete($key);
    }
    elsif ( $cache->keys > $max_size ) {
      $cache->shift;
    }
    $cache->set( $key, $value );  # new values in front
  }
}
Enter fullscreen mode Exit fullscreen mode

While we still have sigils (the punctuation characters) for our variables, if you're familiar at all with Perl, you'll probably note that this is much easier to read than a lot of the legacy Perl that's out there.

Corinna is intended to be standard class-based OOP, but with much of the code reuse provided by "roles." These are Smalltalk-style traits, but with an emphasis on the formal model (pdf) to ensure that ordering and grouping do not affect the behavior, unlike with inheritance or mixins.

The work for this project is focused on building an MVP and iterating on top of that. Thus, many features are omitted for now, but that will give us a chance to see what features we actually need instead of what features are being requested.

Paul Evans has been writing Object::Pad, a testbed for many of the ideas we're implementing and it's been going well. There is broad community support, though there's been some pushback, too. I've also been working with some of the core Perl developers to get buy-in.

If we keep going the way we currently are, I'd cautiously say that we will have a proposal for the Perl core that will have a good chance of being accepted as an experimental feature. Further, Paul intends to release a CPAN module that will allow older versions of Perl to take advantage of the new features.

We don't have a timeframe, but we do have a vision. And so far, it's looking pretty exciting.

Here's a presentation I gave at the 2021 FOSDEM.

As a final note, look at the Cache::LRU code above. Here it is written in core Perl. I know which one I would rather write.

package Cache::LRU;
use strict;
use warnings;
use Carp;
use Hash::Ordered;

sub new {
    my ( $class, %arg_for ) = @_;
    my $cache    = Hash::Ordered->new;
    my $max_size = $arg_for{max_size} || 20;
    unless ( $max_size + 0 > 1 ) {
        croak("Invalid max_size argument: $max_size");
    }
    bless {
        cache    => $cache,
        max_size => $max_size,
        created  => time,
    } => $class;
}

sub created  { return $_[0]->{created} }
sub max_size { return $_[0]->{max_size} }
sub _cache   { return $_[0]->{cache} }

sub set {
    my ( $self, $key, $value ) = @_;
    if ( $self->_cache->exists($key) ) {
        $self->_cache->delete($key);
    }
    elsif ( $self->_cache->keys >= $self->max_size ) {
        $self->_cache->shift;
    }
    $self->_cache->set( $key, $value );
}

sub get {
    my ( $self, $key ) = @_;
    $self->_cache->get($key);
}

1;
Enter fullscreen mode Exit fullscreen mode

Discussion (9)

pic
Editor guide
Collapse
mhd profile image
Michael Dingler • Edited

As with all syntax issues, it's a pretty silly pet peeve, but there's one thing I reallly like better in the core version: There's one indentation level less for most of the file. Most of the time you don't have multiple classes per file, so it's usually quite obvious that you're within class scope and I don't need another 4 char margin (or preferably 8, if I'm not beaten over the the head with PBP by co-workers).

Collapse
ovid profile image
Ovid Author

I know what you mean and people have pointed this out a few times. The reason for this is when I mention that this is designed to be "backwards-compatible, but also allows the overall language to grow."

By having that extra level of indentation and putting the class definition in a block, we guarantee that, unless you're doing something really, really strange, it's backwards-compatible. That's because this code could never have compiled on older versions of Perl. The introduction of that block structure gives us the freedom to do anything in that block we feel is necessary, but companies can rest assured that they can still upgrade without our changing anything that currently exists.

Collapse
mhd profile image
Michael Dingler

As I've mentioned this is only a slight pet peeve and doesn't really impact me all that that much one way or another. Raku using the nested blocks alone would be a good reason for using that.

But I do feel a bit daft nonetheless: A top-level method wouldn't compile either, just like one nested within a class. On the other hand, once you bring in stuff like Moops, Dios or just Function::Parameters, everything is possible.
Having everything isolated in a block is a good visual boundary, definitely agree on that, but "could never have compiled" is a bit hard to parse for me on a Monday 😉

Collapse
jplindstrom profile image
Johan Lindstrom • Edited

I don't understand the argument. How is "it can't compile" backwards compatible? It's new syntax that should blow up in earlier versions. And it does.

Aside from that, a class declaration with / without a block seems to blow up the same way:

class Cache::LRU;            # to end of file
class Cache::LRU { ... };    # to end of block
Enter fullscreen mode Exit fullscreen mode

So what is the argument?

Thread Thread
ovid profile image
Ovid Author

This is something that was pointed out to me (by Sawyer? can't recall) a while ago as an unintended benefit. In short, if I wanted to repurpose syntax such as my Dog $spot, I'd be stepping on existing syntax. However, by creating a new syntax with an unambiguous scope and is guaranteed not to run on older versions of Perl (short of something really bizarre going on), we have a brand new syntax which is guaranteed not to clash with existing usage.

Further, because its scope is well-defined, we can play around with new syntax in that scope. Just adding a has function or a method keyword to the language could break all sorts of existing code that is already trying to do something like that. But by doing it in a new scope, we're safe.

Collapse
cliff profile image
Cliff Stanford

Actually, I'm probably going to get slated for this but I prefer the core version. I'd have written it more concisely but, IMO, it's easier to read and easier to write.

Collapse
ovid profile image
Ovid Author

You're not going to get slated at all! Everyone has their preferences and we won't be removing bless from the core. So if you prefer to write your OO code that way, go right ahead. We won't break it.

Collapse
systemz profile image
Zahir Lalani

Its been a long wait. Really looking forward to this

Collapse
sigzero profile image
sigzero

I am super excited to see this come to fruition. There seems to be very careful thought going into what "Corinna" should be in the core.