Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How do I capture default arguments with `|c`?

Tags:

raku

I've got this function here:

my @modifiers = <command option>;
sub triple(|c(Str:D $app1!, Str:D $app2!, Str:D $key! where .chars == 1, Str:D $mod where ($mod ~~ any @modifiers) = 'command' )) {
    print_template(|c);
}

sub print_template(*@values) {
   ...work done here...
}

The problem I'm having is if I call it without the 4th argument, with something like triple 'App1', 'App2', 'p';, the default $mod argument does not get passed on to the print_template argument.

Is there a way to accomplish this?

For full context, this is the toy program here: https://paste.debian.net/1226675/

like image 575
StevieD Avatar asked Jun 11 '26 21:06

StevieD


2 Answers

TL;DR 1. An explanation of the problem. 2. A DRY solution. 3. The DRYest solution: a parameters-capture function.

An explanation of the problem

A call proceeds in two steps:

  1. Construct a call, without knowing what routine will hear the call. This includes constructing a capture of the call arguments. This capture is already done and dusted, immutable, before step 2.

  2. See what routine candidates there are that might bind to the call. Try to bind the capture to their signatures. Some parameters in a routine's signature might specify defaults in case an argument is missing.

So, given a capture c, you can't alter it to make up for any arguments that aren't in it, and Raku doesn't automatically create a new capture that pretends any arguments that aren't in it are now in it. Instead you're going to have to manually add the missing values.

A DRY solution

The string 'command' appears twice in the suggested solution in your answer.

One way to write a DRY solution is to use the whole capture, which will include all the passed arguments, and then append any parameters for which corresponding arguments were not passed. That is to say, instead of:

my \d = \(|c[0..2], c[3] // 'command');

write this:

my \d = \(|c, |($mod if not c[3]));

The DRYest solution: a parameters-capture function

Ultimately what your scenario calls for is a function which completely ignores the arguments used to call a routine and instead just creates a new capture consisting of all of a routine's parameters. Then one could just write, say:

print_template(|parameters-capture);

That strikes me as pretty non-trivial. It would mean walking a routine's parameter data. This would presumably go via something like &?ROUTINE.signature.params. But &?ROUTINE is a compile-time variable and is relative to the current routine, so how do you get to that in a function you've called from the routine whose parameters you're interested in? And even if you can get to it, how do you get from compile-time parameter data structures to the run-time values that end up bound to the parameters? It's all way past my paygrade. It's perhaps a lot easier to do this sort of guts coding than it is in, say, the Perl world, where it means C coding, but still.

like image 131
raiph Avatar answered Jun 17 '26 23:06

raiph


OK, based on responses in IRC, this does not appear to be possible. One suggested workaround:

sub triple(|c(Str:D $app1!,
              Str:D $app2!,
              Str:D $key! where .chars == 1,
              Str:D $mod where ($mod ~~ any @modifiers) = 'command' )) {
    my \d = \(|c[0..2], c[3] // 'command');
    print_template(|d);
}
like image 43
StevieD Avatar answered Jun 18 '26 01:06

StevieD



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!