In perl, is there a canonical practice or idiom for assigning value to an argument which, if "empty", is assigned a default value?
Here, "empty" is defined as an argument value which is either undef
or the null string (''
). Notably, 0
(or perl false) is a valid, non-"empty" value, which rules out the construction $min_perl_version = (shift ||= '5.008009')
.
This is my current construction (note the use of the v5.10+ logical-or operator, //
):
my $min_perl_version = do {$_ = shift // q{}; (length) ? $_ : '5.008009'};
as in the example function:
sub is_CORE { # ( $:MIN_PERL_VERSION , $:MODULE )
use 5.008009; # perl v5.8.9; required for Module::CoreList to be available in CORE
my $min_perl_version = do {$_ = shift // q{}; (length) ? $_ : '5.008009'}; # "empty" => default to version 5.008009
my $module = shift;
return ($Module::CoreList::VERSION >= 2.99) && Module::CoreList::is_core($module, undef, $min_perl_version);
}
I don't want warnings or to duplicate the use of the default value.
Is there a simpler / more elegant solution, say... without a named temporary variable?
A succinct solution which is functional back to earlier perl versions (5.6 or 5.8) would be a plus as well.
edit: for clarity
For clarification, the function interface and definition of argument validity (eg, what "empty" means) are set constants. While I appreciate any critique, a conversation about the line:
my $min_perl_version = do {$_ = shift // q{}; (length) ? $_ : '5.008009'};
and how it might be better coded is the real point of the question.
2 Answers 2
use 5.008009
is insufficient here, since you're using the //
operator, which was introduced in Perl 5.10.
This function feels awkwardly designed. Generally, parameters that have default values appear after all required parameters. Here, I would need to explicitly pass in undef
as the first parameter to take advantage of the default. Also note that the parameters for your is_CORE
are backwards relative to Module::CoreList::is_core
.
I also don't understand the benefit of having the empty string trigger the default. I can understand having undef
trigger the default. I could also agree with having all "falsy" values trigger the default. But this particular design feels weird. If I had to do it, though, I'd write it like this:
sub is_CORE {
use 5.008009; # perl v5.8.9; required for Module::CoreList to be available in CORE
my ($module, $min_perl_version) = @_;
$min_perl_version = '5.008009' unless defined $min_perl_version && length $min_perl_version;
$Module::CoreList::VERSION >= 2.99 && Module::CoreList::is_core($module, undef, $min_perl_version);
}
After some helpful discussion (notably from PerlMonks), I was led to this construction:
my $arg = List::Util::first { defined && length } ( shift, 'default_value' );
It's DRY. It doesn't introduce any extra temporaries. And, to my eyes, it's simpler, easier to understand, and just more elegant.
As a bonus, it's portable, without requiring installation of any extra modules, back to perl v5.7.3 (the first version including List::Util in perl CORE).
I would refactor the example function as:
sub is_CORE { # ( $:MIN_PERL_VERSION , $:MODULE )
use 5.007003; # perl v5.7.3; required for List::Util to be available in CORE
use 5.008009; # perl v5.8.9; required for Module::CoreList to be available in CORE
use List::Util qw/ first /;
my $min_perl_version = first { defined && length } ( shift, '5.008009' ); # "empty" => default to version 5.008009
my $module = shift;
return ($Module::CoreList::VERSION >= 2.99) && Module::CoreList::is_core($module, undef, $min_perl_version);
}
my ($arg) = @_; $arg = "default_value" if !length($arg // "");
eval.in/624512 \$\endgroup\$5.10
eval.in/624514 \$\endgroup\$