August 2021 Archives

A dream realized

Have you heard that they are finally putting together a proposal to add a clean modern OO system into the core of Perl?

If you haven’t, I strongly encourage you to look over the RFC for Corinna, or at least watch Ovid’s excellent presentation on the project.

It’s reassuring that the list of contributors to the proposed design includes some of the most highly respected names in the Perl community, many of whom have previously taken one (or more!) tilts at this particular object-oriented windmill.

Indeed, over the past two decades I too have repeatedly attempted to design and prototype richer and more robust OO systems for Perl, starting way back in the previous millennium with a brief stint as the maintainer of Class::Struct, and continuing on though the release of modules such as Class::Std, Class::Delegation, and most recently: Dios.

The Dios module represented almost everything I thought a comprehensive Perl OO system should contain. Not surprisingly, that actually meant it simply stole almost everything that Raku’s comprehensive OO system contains (mostly because I had spent quite a lot of time over the past two decades helping design that OO mechanism as well).

The only three problems with Dios are:

  • It’s not built-in to Perl itself, so its performance is suboptional;
  • It’s not built-in to Perl itself, so it requires a huge amount of extremely complex multi-layer code to make it feel as if is;
  • It’s not built-in to Perl itself, so it's merely one possible choice amongst the vast and incoherent array of entirely reasonable alternatives already on CPAN.

That’s why, when people ask me whether I support the Corinna proposal, my response is: OH, HELL YES! I’ve literally been waiting two decades to see Perl gain a proper, built-in, declarative, encapsulated, and performant OO system. And this, I firmly believe, is it.

At this point I could launch into one of my usual extended exegeses on why this particular design is so exceptionally good, but I think two quick and specific examples are actually sufficient to explain my excitement about this proposal.

The first example is purely syntactic. If you were kind enough to watch the Three Little Words video in which I introduced Dios, you’ll have seen how that module vastly improves the syntax for declaring a Perl class. Instead of something like:

  package Account {
      use experimental 'signatures';
      use Carp 'croak';

      state $next_ID = 'AAA0001';

      sub new($class, %arg) {
          croak('Cannot specify ID as an constructor argument')
              if exists $arg{ID};
          bless {
              name    => $arg{name}    // croak('Missing name arg'),
              balance => $arg{balance} // 0,
              ID      => $next_ID++,
          }, $class;
      }

      sub name ($self) {
          return $self->{name};
      }

      sub set_name ($self, $newname) {
          $self->{name} = $newname;
      }

      sub balance ($self) {
          return $self->{balance};
      }

      sub ID ($self) {
          return $self->{ID};
      }

      sub deposit ($self, $amount) {
          $self->{balance} += $amount;
      }

      sub report ($self, $fh = *STDOUT) {
          $fh->say( $self->ID . ': ' . $self->balance );
      }
  }

...Dios allows you to achieve exactly the same functionality with just:

  use Dios;

  class Account {
      state $next_ID = 'AAA0001';

      has $.name     is rw  is required;
      has $.balance  = 0;
      has $.ID       = $next_ID++;

      method deposit ($amount) {
          $balance += $amount;
      }

      method report ($fh = *STDOUT) {
          $fh->say( "$ID: $balance" );
      }
  }

Not only is that three times less code, it’s three times simpler code, which means it’s also likely to be code that’s three times less buggy.

But what really excites me about Corinna is that, under that proposal, the same code would look like this:

  use experimental 'class';

  class Account {

      state $next_ID = 'AAA0001';

      slot $name     :param :reader :writer;
      slot $balance  :param :reader  = 0;
      slot $ID              :reader  = $next_ID++;

      method deposit ($amount) {
          $balance += $amount;
      }

      method report ($fh = *STDOUT) {
          $fh->say( "$ID: $balance" );
      }
  }

In other words: just as concise, just as readable, just as declarative. But without the huge behind-the-scenes overheads that Dios currently imposes.

The second example that illustrates why I’m so enthusiastic about the Corinna proposal is simply a subset of the first example. If we zoom in on that deposit() method:

  # under Corinna
  method deposit ($amount) {
      $balance += $amount;
  }

...we can see an important improvement over the current standard Perl version:

  # under standard Perl
  sub deposit ($self, $amount) {
      $self->{balance} += $amount;
  }

...and an even greater improvement over what would be required under Moose or Moo or Object::InsideOut or most other existing OO frameworks:

  # under most OO frameworks
  sub deposit ($self, $amount) {
      $self->balance( $self->balance + $amount );
  }

Under Corinna, the slots of an object are accessed via simple scalar variables (e.g. $balance) , not via a (slower) hash look-up (e.g. $self->{balance} or via (much slower) accessor methods (e.g. $self->balance).

This means that each individual slot access is going to be faster under Corinna, and so almost every method call will likewise be less expensive. For instance, each call to deposit() is likely to be at least three times quicker than under any of the many OO frameworks, because we’re dispensing with the two additional embedded method calls to $self->balance().

More subtly, if we happened to misspell our slot variable:

  # under standard Perl
  sub deposit ($self, $amount) {
      $self->{balence} += $amount;
      # Silent bug (autovivifies useless 'balence' key)
  }

  # under most OO frameworks
  sub deposit ($self, $amount) {
      $self->balance( $self->balence + $amount );
      # Runtime exception ("No such method 'balence'...")
  }

  # under Corinna
  method deposit ($amount) {
      $balence += $amount;
      # Compile-time exception: ("No such variable: $balence...")
  }

I don't know about you, but I'd much rather discover this problem the first time I compile the code, rather than at the first time it happens to actually be executed or, even worse, after a long and fruitless day of trying to debug the mysterious absence of recent deposit amounts.

There are so many other reasons to be enthusiastic about this proposed addition to standard Perl, not the least of which is that it will become standard and universally available, which may finally cut through the endless debate over whether your next project should use Moose or Moos or Moo or Mu or Spiffy or Object::InsideOut or Class::InsideOut or Class::Simple or Class::Easy or Class::Tiny or Class::Std or Dios or...

So, yes, I’m very excited about the RFC for Corinna, and I strongly encourage you to take a look for yourself at the powerful new OO features that will soon be proposed for Perl.

About Damian Conway

user-pic