I have this code with two functions, the second function has some named parameters with default values:
sub func1($x,$y) {
# do something
}
sub some_func($x, $y , :$vel = 1, :$acceleration = 1/$vel) {
# if $vel and $acceleration both are not missing
# call func1($x, $y)
# else do something else
}
As we know the second function can be called in following ways:
some_func($x, $y); #vel and acceleration are missing
some_func($x, $y, vel => 2, acceleration => 5); #vel and acceleration are not missing
In second call, named parameters: vel
and acceleration
are passed. How can I check within the function body that these
two named parameters are being passed?
Readings:
Keeping default values for nested named parameters
https://docs.raku.org/type/Signature
Why does constraining a Perl 6 named parameter to a definite value make it a required value?
There are multiple ways to achieve this, though – as far as I know – all of them involve changing &some-func
's definition (not just its body).
I'd probably do it like this: split the function into a multi
with one candidate that handles the case where both arguments are passed. You can do that by marking the named arguments as required
for that candidate a !
. Here's how that would look:
multi some-func3($x, $y, :$vel!, :$acceleration!) {
note "Both arguments passed"
}
multi some-func3($x, $y, :$vel = 1, :$acceleration = 1 ÷ $vel) {
note "Do something else"
}
Or you could do it by dropping the default values from the function signature and then setting them in the body (this solution will likely look familiar to anyone used to a programming language that doesn't support default values). Note that modifying $vel
and $acceleration
requires marking them as is copy
.
sub some-func($x, $y, :$vel is copy, :$acceleration is copy) {
if $vel.defined && $acceleration.defined {
note "Both not missing"
} else {
$vel //= 1;
$acceleration //= 1 ÷ $vel;
note "Do something else"
}
}
Another approach would be to capture all of the arguments to some-func
before assigning defaults and then to inspect the Capture
. That might look like:
sub some-func2(|c ($x, $y, :$vel = 1, :$acceleration = 1 ÷ $vel)) {
when c<vel> & c<acceleration> ~~ Any:D { note "Both args passed" }
note "Do something else"
}
(Note that this approach gives slightly worse error messages when some-func
is passed the wrong arguments)
Or you could give each default value a marker role
that you can test against in the function body:
my role MyDefault {}
sub some-func5($x, $y, :$vel = 1 but MyDefault,
:$acceleration = (1 ÷ $vel) but MyDefault) {
when $vel | $acceleration ~~ MyDefault { note "Do something else"}
note "Both args passed"
}
Or, a slight variant of the above: use a string literal to auto-generate a role rather than defining one manually. This is slightly less typesafe – it doesn't protect against typos in the role name – but is also a bit more concise.
sub some-func($x, $y, :$vel = 1 but 'default',
:$acceleration = (1 ÷ $vel) but 'default') {
when $vel | $acceleration eq 'default' { note "Do something else"}
note "Both args passed"
}
Like I said, I'd go with the multi
. But I hope that seeing several different ways helps you think about the problem – and maybe even teaches a bit of more advanced Raku syntax.