How to provide a non-slurpy array or named array from the command line?
Asked Answered
C

1

12

First of all: raku (perl6) is amazing. And so is Cro. It only took a week-end to fall in love. However now I stumble over something that must be extremely simple.

If I use a slurpy parameter in a multiple dispatch MAIN this is recognized and works perfectly:

multi MAIN( 'config', 'add', *@hostnames ) {

However if I make this a non-slurpy array, this is either not recognized or I don't know how to provide it from the command line:

multi MAIN( 'config', 'add', @hostnames ) {

I would expect one of these invocations to work:

$ cli.raku config add www.example.com example.com
$ cli.raku config add www.example.com,example.com
$ cli.raku config add www.example.com, example.com

A similar construct is used in the Cro CLI however without example of how to call one of the commands with an array in the docs.

I also tried this with an array as named parameter:

my %*SUB-MAIN-OPTS = :named-anywhere;
multi MAIN( 'config', 'add', :@hostnames) {

Given the example in the raku docs I would expect this to work:

$ cli.raku config add --hostnames=www.example.com example.com

But it does not, nor variants with comma or space comma separation. In all cases I get the usage information.

Constant answered 4/6, 2020 at 22:41 Comment(2)
For the named argument I think you give the option multiple times: —host=a —host=bBiamonte
The main problem here is that there's a MAIN with an array as an argument would expect to be handled an array by its invocant and it's impossible that the shell does that kind of thing (see here, so you're left with the slurpy. Commas do not make a difference. Maybe we should clarify that in the documentation, but at any rate, it's a shell limitation, not Raku'sDenudation
H
10

The arg parsing that is built into Raku corresponds to standard shell features/conventions. As JJ notes, there is no shell feature/convention for individual arrays. I presume this is why plain @foo (and %bar) is not defined to match anything as part of the built in CLI parsing features.


Your example would be covered by a slurpy, and you haven't said why you don't want to use a slurpy.

One guess is that it's because a slurpy would allow zero arguments. Here's an idiomatic way to fix that:

multi MAIN( 'config', 'add', *@hostnames where +*) {

You can read the +* as "one or more".

What's actually going on there is that I've written a where clause. This is a constraint that's imposed on a variable or parameter in addition to any other constraint such as a type. A where clause is an arbitrary condition that evaluates as True or False. The value that is about to be bound to the variable/parameter (if it passes the constraint condition) is implicitly "it" for the condition.

Whenever an expression contains one or more operator(s) combined with one or more *s as operand(s), Raku converts the expression into a function, where the *(s) are parameters of that function.

So +* is a tiny little one parameter function that just applies a prefix + to its one argument aka "it".

When you apply prefix + to an array, it returns the Int count of elements in that array. The value returned from the condition expression is evaluated as a Bool -- True or False. If it's 0 (i.e. no arguments were passed), the constraint condition returns False so the MAIN signature fails to bind and the usage message gets displayed.


If that's not it, perhaps it's because you can use only one array slurpy per command line, at the end.

Or just curiosity.


A named array works like this:

sub MAIN ( :@n ) {}

my shell prompt> cli-prog.raku -n=www.example.com -n=example.com

A search of SO for "[raku] getopt"


You can take over control of CLI parsing to get whatever result you want:

Hokum answered 5/6, 2020 at 0:16 Comment(10)
That there is no shell convention for individual arrays as @Denudation mentioned clarified a lot.Constant
In the SuperMain source, you pointed me to, I found this: PROCESS::<%SUB-MAIN-OPTS><named-anywhere> = True; That allows me to put the subcommands in front of the named array. (And perhaps could be added to docs.raku.org/language/create-cli#index-entry-$PERCENT_SIGN*SUB-MAIN-OPTS)Constant
And thanks also for the video and presentation of Brian Duggan, very informative!Constant
"perhaps PROCESS::<%SUB-MAIN-OPTS><named-anywhere> = True; could be added to docs" Doc currently suggests my %*SUB-MAIN-OPTS = :named-anywhere;. That sets the same variable. (But only for same process code called from scope containing the my (and any code that that code calls etc.).) Sounds like you're saying that that's not global enough for the way you've written your code? I've searched doc issues and don't see a report about that. Please consider opening a doc issue and explaining there. Thanks. Welcome to Raku. :)Hokum
No, just tested, using a slurpy array both work fine in the same scope as MAIN. There is a difference though. If I use a named array-reference :h( :$hostnames ) where {$_.all ~~ URI::Host} instead of a slurpy array, then ( PROCESS::<%SUB-MAIN-OPTS><named-anywhere> = True; works and my %*SUB-MAIN-OPTS = :named-anywhere; doesn't. Don't know why yet. And thanks!Constant
Hi @acw. Thanks for following up. Are you putting the my inside the MAIN, or outside, before, it? And is it in the file executed by raku or in a module?Hokum
In both cases outside of MAIN, just before it. So in the same scope as MAIN. It's in a module. Inspired by the Cro CLI, I have a script file in bin that has nothing but:#!/usr/bin/env raku use MyApp::Tools::CLI; Within this CLI.pm6 there's my MAIN.Constant
@acw. It would be really helpful if you wrote that up as a separate SO, including a minimal reproducible example. On its face it must be something to do with the distinction between a somewhat global global and a PROCESS global (as global as it gets).Hokum
No problem @raiph. Posted this question: Alter how arguments are processed before they're passed to sub MAINConstant
Note that the Raku convention of repeating the named parameter works well in conjunction with the brace-expansion feature present in bash, zsh, etc. For the example at the end of the answer, this would work from those shells with the feature enabled: cli-prog.raku --n={www.example.com,example.com,other.url,even-more.org}Woolson

© 2022 - 2024 — McMap. All rights reserved.