ETOOBUSY š minimal blogging for the impatient
Raku - default member values
TL;DR
An example of a Raku class with default values for members.
This post is probably trivial for most, but I guess Iāll come back looking at it several times. Hi future Flavio!
I wanted to know how to handle default values for member variables in a class. It turns out that itās managed the way I was expecting it - i.e. using signatures to specify default values.
Thereās still some ācargo cultā I have regarding to when I have to use
$!member
and when $.member
, but I trust Iāll get the hang of it.
Hereās the example:
class DefaultedMember {
has $!member;
has $!other-member;
has &!callback;
has @!items;
submethod BUILD (
:$!member = 'whatever',
:&!callback = { '[' ~ $^a ~ ']' },
:$!other-member,
:@some-items,
) {
$!member = 'fixed-prefix-' ~ $!member if $!member ~~ /hello/;
$!other-member //= 'hey!';
@!items = $!member;
self.add-to-items(@some-items);
}
method add-to-items (*@new-items) {
@!items.push: @new-items.Slip;
}
method talk {
put &!callback($!member), ' ', $!other-member, ' ', @!items.gist;
}
}
All member variables are private (declared with the !
twigil). Our
goal is to make sure all of them have the right value when the object
is initialized.
For my purposes, the BUILD
method
/submethod
proved sufficient. I
still donāt know when I should use one or the other, but I hope Iāll get
the hang of it shortly. Other alternatives, as I understand, are the
TWEAK
method
/submethod
and a brand new new
method, which is the
last resort for complex things (I guess).
The most straightforward way of providing a default value is to put it
directly in the signature for BUILD
. My understanding is that naming
the variable in the signature the same as the member variable (and
prefixing it with :
) makes BUILD
initialize the member itself as we
expect. This happens before we enter the code block associated to
BUILD
. Another way is to use the code block itself.
Letās see what happens for the different members:
$!member
is initialized from the signature, so entering the block it either has the value passed in from the invocant, or the default valuewhatever
. It is further conditionally modified inside the block, so for example if the input value is the stringhello, world!
, its value becomesprefix-hello, world!
.&!callback
is initialized from the signature only, getting either the value passed by the invocant, or the default code block provided directly in the signature;$!other-member
is initialized by the signature, getting either the value passed from the invocant, or anAny
value (I guess). For this reason, we then initialize it inside the block in the case that the value is not defined. This shows that we can do complex initialization of a variable when its value is not passed in;@!items
cannot be initialized through the signature, but its value is computed based on other member variables ($!member
and$!other-member
) as well as an additional, optional input parameter@some-items
, which are set by invoking methodadd-to-items
. This shows that:- itās possible to have a constructor signature that does not necessarily need to reflect the internal structure of the class, which is much appreciated;
- itās possible to call other methods from
BUILD
, which is much appreciated as well!
Hereās an example sequence of invocations, as well as their result:
DefaultedMember.new.talk;
# OUTPUT: ļ½¢[whatever] hey! [whatever]ā¤ļ½£
DefaultedMember.new(member => 'hello, world!').talk;
# OUTPUT: ļ½¢[prefix-hello, world!] hey! [prefix-hello, world!]ā¤ļ½£
DefaultedMember.new(
member => 'hi there!',
callback => { 'Ā«' ~ $^input ~ 'Ā»' },
).talk;
# OUTPUT: ļ½¢Ā«hi there!Ā» hey! [hi there!]ā¤ļ½£
DefaultedMember.new(
member => 'hi there!',
callback => { 'Ā«' ~ $^input ~ 'Ā»' },
other-member => "I'm here too!",
).talk;
# OUTPUT: ļ½¢Ā«hi there!Ā» I'm here too! [hi there!]ā¤ļ½£
DefaultedMember.new(
member => 'hi there!',
callback => { 'Ā«' ~ $^input ~ 'Ā»' },
other-member => "I'm here too!",
some-items => < and here we go >,
).talk;
# OUTPUT: ļ½¢Ā«hi there!Ā» I'm here too! [hi there! and here we go]ā¤ļ½£
Now Iām left with a few doubts:
- should I declare
BUILD
as amethod
or as asubmethod
? Iāve seen both in several examples, and Iām not sure when I need either one; - should I even use
BUILD
to do this, or should I useTWEAK
? - When do I need to completely override
new
? (I have an idea regarding thisā¦)
If you made it so far, and know the answer, and have 5 minutes to
spareā¦ ring a bell in the comments or by email to flavio @t
polettix.it
!