-
Notifications
You must be signed in to change notification settings - Fork 19
Constructors
Click here to provide feedback.
To better understand this document, you should read Class Phases.
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.
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.
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.
Nothing is set in stone.
- All Corinna constructors take a an even-sized list of named arguments.
- 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. - The constructor is named
new
. - Slots (instance variables) are assigned values from constructor arguments according to their corresponding
:new
attributes. This is done internally via theNEW
method. - Unknown attributes passed to the constructor are fatal unless a
CONSTRUCT
phase is supplied to handle (and remove) those arguments -
Before the constructor is called, we invoke an implicit
CONSTRUCT
phase from theUNIVERSAL::Corinna
class. TheCONSTRUCT
block is required to return an even-sized list of key/value pairs. -
After the constructor is called, we invoke an implicit
ADJUST
phase from theUNIVERSAL::Corinna
class - Constructor arguments are named after the slot identifier, unless a
:name($new_name)
attribute is supplied.
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);
}
Corinna—Bringing Modern OO to Perl