I'm quite fond of using PerlX::Maybe to pass parameters to subroutines if they are there:
GetOptions(
'f' => \my $filename,
'bar' => \my $bar,
'bruce' => \my $batman,
'u' => \my $universe,
);
foo(
file => $filename,
maybe bar => $bar,
maybe baz => $batman,
maybe universe => $universe,
)
The above will invoke foo() with only the hash keys that have a defined value. This is better in the sense that this allows foo() to make a difference between parameter was not passed at all and parameter was passed, but was undef:
sub foo( %options ) {
if( ! exists $options{ baz } ) {
$options{ baz } = 'Superman';
};
...
}
Now, I'd like to have something similar(ish) to maybe on the receiving side for subroutines and objects, but I'm lacking a good name and a good syntax for it. The idea is to only set a value in a hash if the key does not exist yet. This is different from the // operator, because that one only checks for defined-ness, not for existence.
If we already had full-grown subroutine keyword parameters, this could be written in a declarative way as:
sub foo( :$baz = 'Superman', :$bar, :$file, :$universe='DC' ) {
}
(actually, I'm not sure if the above is correct for an invocation foo( baz => undef )).
But we don't have named parameters in the syntax yet, so we have to deparse the parameters ourselves:
sub foo( %options ) {
...
}
Let's assume that option is a good name for this (I'm not convinced):
sub option ( $key, $def, $options ) {
if( !exists $options->{ $key } ) {
$options->{ $key } = $def;
}
return $options
}
Then we could have a syntax like this:
sub foo( %options ) {
option baz => 'Superman',
option universe => 'DC',
\%options;
return \%options
}
But I'm not entirely happy with this approach for two reasons:
- It doesn't strike me as overly elegant. I'm happy with it being not overly elegant, but there are some warts:
- There must be something like this on CPAN already
Test file:
use 5.020;
use feature 'signatures';
no warnings 'experimental::signatures';
use Carp 'croak';
sub option ( $key, $def, $options ) {
if( !exists $options->{ $key } ) {
$options->{ $key } = $def;
}
return $options
}
sub required ( $key, $options ) {
if( !exists $options->{ $key } ) {
croak sprintf 'Need the "%s" option passed in', $key;
}
return $options
}
sub foo( %options ) {
option baz => 'Superman',
option universe => 'DC',
\%options;
return \%options
}
sub req( %options ) {
required 'run',
option baz => 'Superman',
\%options;
return \%options
}
use Test2::V0 '-no_srand';
is option('baz' => 'default', {}), { baz => 'default' }, 'Setting a de
+fault';
is foo(), { baz => 'Superman', universe => 'DC' }, 'Default values';
is foo(baz => 0), { baz => '0', universe => 'DC' }, 'False value';
is foo(baz => undef), { baz => undef, universe => 'DC' }, 'Undef but p
+resent value is kept';
is foo(baz => undef), { baz => undef, universe => 'DC' }, 'Undef but p
+resent value is kept';
ok dies { req( baz => 'Wonderwoman' ) }, 'A missing obligatory option
+dies';
ok lives { req( run => undef, baz => 'Wonderwoman' ) }, 'Passing the o
+bligatory option lives, even if undefined';
#class cfoo 0.01 {
# field $baz;
# field $universe;
#
# method foo( $self, %options ) {
# option baz => $self->baz,
# option universe => $self->universe,
# \%options;
#
# return \%options
# }
#}
#my $f = cfoo->new( baz => 'objSuperman', universe => 'DC' );
#is $f->foo( baz => undef ), { baz => undef, universe => 'meth_DC' },
+'Positional parameters work';
done_testing;