Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to require 1 or more of an argument in MAIN

Tags:

raku

Right now, I have a MAIN sub that can take one or more string arguments. But I am using two separate parameters for MAIN to do it:

sub MAIN (
    Str:D $file,
    *@files,
) {
    @files.prepend: $file;

    # Rest of the program
}

Now I am wondering if there's a more idiomatic way to achieve this, as my current solution feels a little clunky, and not very Perly.

like image 434
Tyil Avatar asked May 02 '18 13:05

Tyil


3 Answers

You could do it with a proto sub

proto sub MAIN ( $, *@ ){*}

multi sub MAIN ( *@files ) {
    # Rest of the program
}

or with sub-signature deparsing

sub MAIN ( *@files ($,*@) ) {
    # Rest of the program
}
like image 137
Brad Gilbert Avatar answered Oct 16 '22 15:10

Brad Gilbert


At the risk of "over answering" - my take on "Perly" is concise as possible without becoming obscure (perhaps I'm just replacing one subjective term with two others... :-)

If you have a "slurpy" array as the only parameter, then it will happily accept no arguments which is outside the spec you put in the comments. However, a positional parameter is compulsory by default and proto's are only necessary if you want to factor out constraints on all multi's - presumably overkill for what you want here. So, this is enough:

sub MAIN($file , *@others) {
    say "Received file, $file, and @others.elems() others."
} 

This is close to what mr_ron put - but why not go with the default Usage message that MAIN kindly whips up for you by examining your parameters:

$ ./f.pl
Usage:
  ./f.pl <file> [<others> ...]

Some might say I cheated by dropping the Str type constraint on the first parameter but it really doesn't buy you much when you're restricting to strings because numerics specified at the CLI come through as type IntStr (a kind-of hybrid type) that satisfies a Str constraint. OTOH, when constraining CLI parameters to Num or Int, Perl6 will check that you're actually putting digits there - or at least, what unicode considers digits.

If you're wanting actual filenames, you can save yourself a validation step by constraining to type IO(). Then it will only work if you name a file. And finally, putting where .r after the parameter will insist that it be readable to boot:

sub MAIN(IO() $file where .r, *@others) { ...

One short line that insists on one compulsory argument that is a filename referencing a readable file, with a varying number of other parameters and a useful Usage message auto generated if it all goes sideways...

like image 4
Marty Avatar answered Oct 16 '22 15:10

Marty


Perhaps good enough answer here:

sub MAIN(*@a where {.elems > 0 and .all ~~ Str}) {
    say "got at least 1 file name"
}

sub USAGE {
    say "{$*PROGRAM-NAME}: <file-name> [ <file-name> ... ]"
}

Based on docs here: https://docs.perl6.org/type/Signature#Constraining_Slurpy_Arguments

like image 4
mr_ron Avatar answered Oct 16 '22 17:10

mr_ron