This question concerns Perl's class
syntax (new as of Perl 5.38.0). I have two classes, Rectangle
and Square
, with Square
inheriting from Rectangle
. They have different constructor arguments; Rectangle
expects length
& height
while Square
expects just side
. My hope was that Square
would be able to inject the length
and height
arguments into the constructor for Square
's parent Rectangle
object, based on side
's value. Something akin to this, though the BUILD
syntax from Object::Pad
doesn't work:
class Rectangle
{
field $length :param;
field $height :param;
method get_area { return $length * $height; }
}
class Square :isa(Rectangle)
{
field $side :param;
BUILD {
my(%args) = @_;
my($side) = $args{'side'};
push(@_, length => $side, height => $side);
}
}
Here's my test script:
my($rect) = Rectangle->new(length => 5, height => 6);
say $rect->get_area();
my($sq) = Square->new(side => 5);
say $sq->get_area();
My expected output would be:
30
25
Is there a way to do this in 5.38.x? I'm thinking that the class
implementation is simply not evolved enough yet to do this.
The only way to hook into instantiation provided by the new class feature are ADJUST blocks. ADJUST blocks have no access to the arguments of the constructor call. They can see $self
and instance variables of their own package. Also they are invoked 'top down' (parent class first). So to make your example work you have to:
This results in
use v5.38;
use feature 'class';
class Rectangle
{
field $length :param = 0;
field $height :param = 0;
method set_length($arg) { $length = $arg }
method set_height($arg) { $height = $arg }
method get_area { return $length * $height; }
ADJUST {
say "in Rect ADJUST";
}
}
class Square :isa(Rectangle)
{
field $side :param;
ADJUST {
say "in Square ADJUST";
$self->set_height($side);
$self->set_length($side);
}
}
my($rect) = Rectangle->new(length => 5, height => 6);
say $rect->get_area();
my($sq) = Square->new(side => 5);
say $sq->get_area();
prints
in Rect ADJUST
30
in Rect ADJUST
in Square ADJUST
25
This feels like an ugly workaround to get past the limitations of the class feature as it is. I would not use it.
Another way to solve this would be to make the length and height attributes overridable by using accessor methods in Rectangle::get_area
:
use v5.38;
use feature 'class';
class Rectangle
{
field $length :param = 0 ;
field $height :param = 0 ;
method get_length { $length }
method get_height { $height }
method get_area { return $self->get_length * $self->get_height; }
}
class Square :isa(Rectangle)
{
field $side :param;
method get_length { $side }
method get_height { $side }
}
my($rect) = Rectangle->new(length => 5, height => 6);
say $rect->get_area();
my($sq) = Square->new(side => 5);
say $sq->get_area();