Home > Back-end >  Raku: How to initialize attributes inherited in a sub-class?
Raku: How to initialize attributes inherited in a sub-class?

Time:07-21

use v6.d;

class Foo {
    has $.name;

    submethod BUILD (:$!name = 'John') {};
}
my $f = Foo.new;
say $f;
# OUTPUT: Foo.new(name => "John")

That works as expected. When I add:

class Bar is Foo {
    submethod BUILD (:$!name = 'Jane') {};
}
my $b = Bar.new;
say $b;

===SORRY!=== Error while compiling scratch.raku
Attribute $!name not declared in class Bar
at scratch.raku:14

How can I assign default values at construction time?

Thank you, Jim

CodePudding user response:

Here's an approach using TWEAK adapted from brian d foy's book, "Learning Raku" (formerly "Learning Perl6"), Chapter 12: "Classes":

use v6.d;

class Foo {
    has $!default-name = 'John';
    has $.custom-name  is rw;

    submethod TWEAK (:$custom-name) {
      self.custom-name = $custom-name // $!default-name;
      };
}

my $f = Foo.new;
say $f; 
put "This is class Foo with {$f.custom-name} as name.";


class Bar is Foo {}

my $b = Bar.new;
$b.custom-name = 'Jane';
say $b;
put "This is class Bar with {$b.custom-name} as name.";

Output:

Foo.new(custom-name => "John")
This is class Foo with John as name.
Bar.new(custom-name => "Jane")
This is class Bar with Jane as name.

Above class Foo's "John" takes the default-name when no custom-name is assigned. In the second example class Bar's "Jane" takes the custom name assigned to it.

You can modify class Bar to have its own default-name, and its own TWEAK submethod. Then the only difference between the original class Foo and the inherited class Bar is Foo {} appears to be declaring $.default-name as a public method.

https://www.learningraku.com/2018/08/14/table-of-contents/
https://www.oreilly.com/library/view/learning-perl-6/9781491977675/

CodePudding user response:

Very close. I think this is what you are after:

use v6.d;

class Foo {
    has $.name = 'John'
}

class Bar is Foo {
    submethod TWEAK(:$name) {}
}

Foo.new.say;                 # OUTPUT: Foo.new(name => "John")
Bar.new(name=>'Jane').say;   # OUTPUT: Bar.new(name => "Jane")

The magic is that special methods new, BUILD and TWEAK will auto set named attrs based on their signature alone. (Actually you get the same outcome if you use BUILD instead of TWEAK in this example.)

Here's why I prefer to use TWEAK:

BUILD may set an attribute, but it does not have access to the contents of the attribute declared as its default as they are only applied later. TWEAK on the other hand is called after default values have been applied and will thus find the attributes initialized. So it can be used to check things or modify attributes after object construction.

  • Related