Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Passing parameters to parent constructor using Perl's new class syntax

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.

like image 671
Joe Casadonte Avatar asked Oct 29 '25 10:10

Joe Casadonte


1 Answers

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:

  1. make the params to the parent class optional
  2. add accessor methods to the parent class
  3. use an ADJUST block in the child to set the parents fields

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();
like image 140
clamp Avatar answered Nov 01 '25 09:11

clamp



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!