I have a method in a class to return a hash, with the aim that children classes will over-ride the method. But for testing purposes, I want a different hash.
When the class is instantiated, I would prefer for the test option to be assumed to be False, so :test can be omitted when not needed.
At present I do this:
class A {
has %.tmps;
submethod BUILD( :$test = False ) {
%!tmps = $test ?? self.test-tmps !! self.default-tmps
}
method test-tmps { ... }
method default-tmps { ... }
}
This does what I want, in that my A $x .= new calls default-tmps, and my A $y .=new(:test) calls test-tmps.
But I wondered about removing the explicit check of $test via multi-dispatch. Except I could not work out the appropriate syntax. I tried
class A {
has %.tmps;
submethod BUILD( :$test = False ) {
%!tmps = self.get-tmps( :$test )
}
multi method get-tmps( :test($)! where * ) {
# return the hash for test values
}
multi method get-tmps( :test($)! where ! * ) {
# return the hash for default values
}
}
This does not work because I always get the test values, whether or not I specify :test in new. So my questions:
How to select the multi method candidate based solely on a boolean's value?
If what I am trying to do is possible, is there a reason (eg. run time / compile time checks) why an explicit check in
BUILD, called bynew, would be better than multi dispatch candidate selection?
And then, if multi-dispatch works, would the following work, or would $!test be False because its undefined when %.tmps is built?
class A {
has Bool $!test;
has %.tmps = self.get-tmps( :$!test );
submethod BUILD( :$!test = False ) {}
multi method get-tmps( :test($)! where * ) { ... }
# replacing the following by whatever is the correct syntax
multi method get-tmps( :test($) where ! * ) { ... }
}
First of all, nowadays I would recommend using the
TWEAKmethod, rather than theBUILDmethod. The TWEAK semantics are generally more DWIM.Secondly, there is nothing special about the
TWEAKmethod (or theBUILDmethod, for that matter). So they can bemulti!So that brings me to the following solution:
Note the
!in:$test!. This makes the named argument mandatory. So that candidate will be selected whenever thetestnamed argument is specified. But this also includes when it is specified with aFalsevalue, as in:!test. That's why it needs to be tested for in that candidate.Also note the
%_inself.TWEAK(|%_). All methods in Raku have an implicit*%_(slurpy hash) parameter defined. So you can use%_inside a method to indicate all arguments that were not caught by an explicit named parameter (such as:$testin this case). Soself.TWEAK(|%_)is basically re-dispatching without all explicit named parameters.Finally: the
--> Nilis just there to indicate that the method will not return a value. This may allow the compiler to produce more efficient bytecode.