Reputation: 3545
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?
Upvotes: 4
Views: 115
Reputation: 32414
TL;DR For most use cases I'd go with @codesections' answer. But someone might appreciate this approach, which uses wrap
.
Leave the existing some_func
definition completely as is. Then add code like this:
&some_func .wrap: sub (|args) {
if args<vel acceleration> .all { func1 |args.list }
else { nextsame }
}
Job done.
How can I check within the function body that these two named parameters are being passed?
You can do pretty much anything you want to do if you're willing to abuse Raku's slang capabilities to change the language.
But short of that, as far as I know you either need to change &some-func
's definition, or leave it unchanged. @codesections showed some ways to change it. Here I show new code you can add to produce a similar effect:
Add this code:
&some_func .wrap: sub (|args) {
if args<vel acceleration> .all { func1 |args.list }
else { nextsame }
}
(See wrap
, |args
, <foo bar>
, .all
, .list
, nextsame
for more info.)
Now you can call it like this:
some_func 42, 99;
some_func 42, 99, vel => 1;
some_func 42, 99, acceleration => 2;
some_func 42, 99, vel => 3, acceleration => 4;
And it'll work as you specified.
Upvotes: 1
Reputation: 9600
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.
Upvotes: 4