Skip to content

Constructors

Ovid edited this page Sep 14, 2021 · 23 revisions

Please see the main page of the repo for the actual RFC. As it states there:

Anything in the Wiki should be considered "rough drafts."

Click here to provide feedback.

To better understand this document, you should read Class Phases.

Version 2

Thanks to Damian Conway for pointing out some problems with the first constructor approach.

Note that Moo/se BUILDARGS and BUILD have been renamed to CONSTRUCT and ADJUST, respectively, because their semantics don't entirely match either Moo/se or Raku.

The Constructor

This is a first pass at declaring how constructors work, taking into consideration known limitations with the attributes. Specifically:

  • You cannot declare constructor arguments unless they have a corresponding slot.
  • We don't have first-class constructors in Perl.

Constructor attributes

Slots are defined via has $varname;. This does nothing outside of declaring the slot. Nothing at all. Instead, if we wish to specify arguments for the constructor, we use the :new syntax: has $varname :new;.

Not passing any of these to the constructor means the value may not be passed to the constructor, but the CONSTRUCT phase can compensate for that.

Constructor Behavior

Nothing is set in stone.

  1. All Corinna constructors take a an even-sized list of named arguments.
  2. A single argument hashref, as allowed in Moose, is not allowed unless a CONSTRUCT phase intercepts it and rewrites it to an even-sized list.
  3. The constructor is named new.
  4. Slots (instance variables) are assigned values from constructor arguments according to their corresponding :new attributes. This is done internally via the NEW method.
  5. Unknown attributes passed to the constructor are fatal unless a CONSTRUCT phase is supplied to handle (and remove) those arguments
  6. Before the constructor is called, we invoke an implicit CONSTRUCT phase from the UNIVERSAL::Corinna class. The CONSTRUCT block is required to return an even-sized list of key/value pairs.
  7. After the constructor is called, we invoke an implicit ADJUST phase from the UNIVERSAL::Corinna class
  8. Constructor arguments are named after the slot identifier, unless a :name($new_name) attribute is supplied.

Explanation

Let's consider this Box class.

class Box {
    has ($width, $height, $depth) :new :reader :isa(Num);
    has $volume :reader :builder;
    method _build_volume() { $width * $height * $depth }
}

my $box = Box->new(height => 7, width => 7, depth => 7);
say $box->volume;   # 343

But maybe we want to call Box->new(7) to get a cube. We do this by overriding the CONSTRUCT phase.

CONSTRUCT (@args) {
    if ( @args == 1 ) {
        my $num = $args[0];
        @args = map { $_ => $num } qw<height width depth>;
    }
    return @args;
}

If you don't like messing around with CONSTRUCT, just create an alternate constructor:

shared method new_cube ($class, $length) {
    $class->new(length => $length);
}