Hello esteemed folks,

As few posts running around these days I propose you an idea I had recently and that seems to work as I expected.

The goal is, as the title says, to use GetOptionsFromArray from Getopt::Long to parse arguments passed to a sub adding the flexibility in arg specification and adding also a primitive type check.

In the below code the sub test (yeah! what a great name! ;) defines the hash %opts where initial default values are declared and where GetOptionsFromArray will soon put passed in arguments and defines also a @template to hold definitions of various arguments type (with the benefit of a minimal type check for free)

Here my actual code:

use strict; use warnings; use Getopt::Long qw(GetOptionsFromArray); use Data::Dumper; my $debug = 0; # 0-2 Getopt::Long::Configure ("debug") if $debug > 1; sub test{ if ($debug){ print "\@_ received: ",Dumper \@_; } # default values in the destination hash my %opts = ( len => 42, switchx => '', # colors => [qw(red yellow green)], # these are NOT ov +erwritten, why? colors => [], ); # copy for debug purpose my %default = %opts; # template for valid options my @template = ( "len|lunghezza=i", "switchx", "tick=i", "colors=s@", ); my $ret = GetOptionsFromArray(\@_, \%opts, @template ) ; # handle errors unless($ret){ print "template for this sub is:\n", "\t",(join "\n\t", @template),"\n", "default values are:\n", ( map{ "\t$_ = ". ( ref ($default{$_}) eq 'ARRAY' ? (join ' ',@{ +$default{$_}}) : $default{$_} ). "\n"}sort keys %default ),"\n"; return; # this should be die } if ($debug){ print "GetOptionsFromArray returned: $ret\n"; } if ($debug){ print "\@_ after GetOptionsFromArray: ", Dumper \ +@_; } print "final \%opts result: ", Dumper \%opts; print "remaining in \@_:", Dumper \@_ if @_; } foreach my $argtest ( [( qw(-len 1111) )], [( qw(-l 11) )], [( qw(-lunghezza 12121212) )], [( qw(-len AAAA) )], [( qw(-len 2222 -un known) )], [( qw(-len 2222 -tick 3333) )], [( qw(-len 2222 -colors blue -colors cyan -colors orange) )], [( qw(-len 2222 --switchx) )], [( qw(-len 3333 --switchx -- Alpha Bravo) )], ){ print "PASSING: (",(join ' ', @$argtest),")\n"; test ( @$argtest ); print "\n"; }

..and the output

PASSING: (-len 1111) final %opts result: $VAR1 = { 'switchx' => '', 'len' => 1111, 'colors' => [] }; PASSING: (-l 11) final %opts result: $VAR1 = { 'switchx' => '', 'colors' => [], 'len' => 11 }; PASSING: (-lunghezza 12121212) final %opts result: $VAR1 = { 'switchx' => '', 'colors' => [], 'len' => 12121212 }; PASSING: (-len AAAA) Value "AAAA" invalid for option len (number expected) template for this sub is: len|lunghezza=i switchx tick=i colors=s@ default values are: colors = len = 42 switchx = PASSING: (-len 2222 -un known) Unknown option: un template for this sub is: len|lunghezza=i switchx tick=i colors=s@ default values are: colors = len = 42 switchx = PASSING: (-len 2222 -tick 3333) final %opts result: $VAR1 = { 'colors' => [], 'len' => 2222, 'tick' => 3333, 'switchx' => '' }; PASSING: (-len 2222 -colors blue -colors cyan -colors orange) final %opts result: $VAR1 = { 'len' => 2222, 'colors' => [ 'blue', 'cyan', 'orange' ], 'switchx' => '' }; PASSING: (-len 2222 --switchx) final %opts result: $VAR1 = { 'colors' => [], 'len' => 2222, 'switchx' => 1 }; PASSING: (-len 3333 --switchx -- Alpha Bravo) final %opts result: $VAR1 = { 'switchx' => 1, 'colors' => [], 'len' => 3333 }; remaining in @_:$VAR1 = [ 'Alpha', 'Bravo' ];

I wonder if this behaviour can be put into a module (the original idea was to use Class::Method::Modifiers to inject code just before the original subroutine) but I wonder if it is worth to and notably which interface to expose. Perhaps something like Sub::Getopt::Long is a nice name for this.

An eventual module interface should be similar to a simple:

use Sub::Getopt::Long qw( longargs ); sub MyOwnStuff{ my %opts = ( len => 1, wid => 2); # defaults %opts = longargs( \%opts, [qw( len=i wid=i )], @_ ); # or simply my %opts = longargs( {len => 1, wid => 2}, [qw( len=i +wid=i )], @_ ); }

Any comment or suggestions are as always warmly welcome!

L*

There are no rules, there are no thumbs..
Reinvent the wheel, then learn The Wheel; may be one day you reinvent one of THE WHEELS.

Replies are listed 'Best First'.
Re: RFC: nicer subs with GetOptionsFromArray from Getopt::Long
by swl (Prior) on Mar 24, 2022 at 00:42 UTC

    It's worth looking at other modules in the Getopt namespace to see if there is prior art that could be adapted (or which might already do this). One that seems to overlap with this proposal is Getopt::Long::Descriptive.

      Thanks for looking swl,

      > It's worth looking at other modules ..

      for sure worth before making this idea a module, I'll investigate it, but I dont think Getopt::Long::Descriptive overlaps with this.

      Infact Getopt::Long::Descriptive as mainly Getopt::Long does is intended to work on @ARGV while my idea is to provide a simple hack to bring these functionality working on @_ or, if you prefere, these modules enhances the program while my idea is to enhance subs usage.

      But anyway thanks for looking.

      L*

      There are no rules, there are no thumbs..
      Reinvent the wheel, then learn The Wheel; may be one day you reinvent one of THE WHEELS.
Re: RFC: nicer subs with GetOptionsFromArray from Getopt::Long ( api design )
by Anonymous Monk on Mar 24, 2022 at 10:58 UTC
      Thanks for the links Anonymous Monk, I'm looking them.

      Maybe due to your conciseness and my basic Enlgish I have doubts on the second part of your post: I'd like you to expand a bit

      > Depending on $ret is a mistake, its a sad part of that api

      The usage of checking $ret (explicit or implicit) aka the return value of the GetOptions call is a well established behaviour. What to do better? Should I wrap into an eval block? Iirc I tried but no $@ was set in case of errors.

      > Sub::Getopt::Long is a wrong namespace prefix

      I never understood how English people set the position of arguments: I'm Latin! Getopt::Long::Sub sounds better? or you are saying that the Getopt::Long part is wrong? Something like Sub::Long::Arguments or what?

      > Work on more use case first. And i don't mean mocks

      The above code is a quick hack: I just covered what it came to my mind in the meanwhile, but it seems these example covering most use cases.

      The assumption is that you finally get your arguments inside a hash (given its flaxibility this should cover most needs, imho). So you would call the futurible method from my unborn module in this way:

      # dest {defaults} [template] + original @_ my %opts = longargs( {len => 1, wid => 2}, [qw( len=i wid=i )] +, @_ );

      Which use cases are you imagining?

      Thanks for looking

      L*

      There are no rules, there are no thumbs..
      Reinvent the wheel, then learn The Wheel; may be one day you reinvent one of THE WHEELS.
        Since this idea relates to functions and their parameters, something similar to Function::Parameters (although that's obviously taken)?

        return value of the GetOptions call is a well established behaviour.

        heh. Almost everything is a fail, sometimes even success. Even correct usage had bugs. Ive observed best use is ignore.

        conciseness

        its a virtue of mobile device :) virtual keyboards are a pain , esp with thumbs

        I never understood how English people set the position of arguments: I'm Latin!

        cpan is first come first serve. And hierarchy goes left to right. Sub:: is wrong prefix .

        Getopt::Long::Sub sounds better?

        Yes but all it says its based on getopt which we from the hierarchy already :) Vanity name Getopt::Long::Discipulus?

        Sub::Long::Arguments or what?

        Why 3 branches? Want 3 words why not Sub::GetoptLong ? Choices are hard even with guidelines Re^2: Naming a module that handles SIP2 (library protocol stopwords) Re: RFC: Automatic logger module.

        ...Which use cases are you imagining?

        one where longargs is nicer or not identical to GetOptionsFromArray , but i struggle