How do I get a mandatory named parameter in sub MAIN?

Viewed 154

This is the best I could get:

sub MAIN(Int :p($parm)!)
{
  say "* parm=", $parm;
}

But:

$ raku test-par.raku
Usage:
  test-par.raku -p[=Int]

It says the parameter is optional!
And indeed it is:

 $ raku test-par.raku -p
 * parm=True

So, what gives ?

3 Answers

A Bool also happens to be an Int:

$ raku -e 'say True.Int'
1

$ raku -e 'say True ~~ Int'
True

Because -p is a Bool it is also an Int:

$ raku -e 'sub MAIN(Int :p($param)) { say $param.raku; say $param ~~ Int; say $param.Int }' -p=42
IntStr.new(42, "42")
True
42

$ raku -e 'sub MAIN(Int :p($param)) { say $param.raku; say $param ~~ Int; say $param.Int }' -p
Bool::True
True
1

$ raku -e 'sub MAIN(Int :p($param)) { say $param.raku; say $param ~~ Int; say $param.Int }' --/p
Bool::False
True
0

The parameter is still optional; -p is just being treated (arguably unintuitively) like -p=1. To actually enforce the requested constraint would unfortunately require adding an additional filter:

$ raku -e 'sub MAIN(Int :p($param) where * !~~ Bool) { say $param.raku; say $param ~~ Int; say $param.Int }' --p=1
IntStr.new(1, "1")
True
1

$ raku -e 'sub MAIN(Int :p($param) where * !~~ Bool) { say $param.raku; say $param ~~ Int; say $param.Int }' -p
Usage:
  -e '...' [-p[=Int where { ... }]]

As some other questions have said True is an Enum with value of 1 so it's and Int if you want to ensure the -p is always called with a value you can use the ARGS-TO-CAPTURE function to check the incoming arguments before they are passed to main. Something like :

sub ARGS-TO-CAPTURE(&main, @args) {
    if none(@args) ~~ /"-p=" \d+/ { 
        say $*USAGE; 
        exit;
    } 
    &*ARGS-TO-CAPTURE(&main,@args)
}

Here we check that at least one of the arguments is -p and it has also got an integer value. You can also override the $*USAGE value by adding the GENERATE-USAGE sub if you want to change how it displays -p in the output.

First of all, thanks for the answers and comments.

For my specific whims, this is the best I could come to:

use v6;

use Getopt::Long;

get-options("p|port=i" => my $port);

sub MAIN
{
  # for the case that no args were passed
  without $port { say "*** error, missing args"; exit }

  say "*** port=$port"
}

As far as I can see, it handles short and long options, and makes the options mandatory.

Related