3
\$\begingroup\$

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.

asked Aug 17, 2016 at 18:21
\$\endgroup\$
4
  • 1
    \$\begingroup\$ Ok, nothing canonical about it; my ($arg) = @_; $arg = "default_value" if !length($arg // ""); eval.in/624512 \$\endgroup\$ Commented Aug 17, 2016 at 19:21
  • 1
    \$\begingroup\$ For perl older than 5.10 eval.in/624514 \$\endgroup\$ Commented Aug 17, 2016 at 19:28
  • 1
    \$\begingroup\$ @rivy np, you can ask this kind of question at stackoverflow.com/questions/tagged/perl \$\endgroup\$ Commented Aug 17, 2016 at 19:58
  • 1
    \$\begingroup\$ I am not at all adverse to rewriting this to be more well-formed for the context of CR. I could pare the question down to show just that single line. But, to me, it's confusing when removed from the context of the enclosing function. Hence, I used a more generic surrounding function initially. When asked for a real-use example, this surrounding function is simply the smallest, easily available use that I found. I've tried to add clarification, and, again, I can rewrite it. I do think code review of a single line / implementation is valid. But tell me if this site is the wrong place for that. \$\endgroup\$ Commented Aug 18, 2016 at 4:51

2 Answers 2

4
\$\begingroup\$

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);
}
answered Aug 17, 2016 at 21:08
\$\endgroup\$
0
3
\$\begingroup\$

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);
}
answered Aug 19, 2016 at 5:45
\$\endgroup\$

Your Answer

Draft saved
Draft discarded

Sign up or log in

Sign up using Google
Sign up using Email and Password

Post as a guest

Required, but never shown

Post as a guest

Required, but never shown

By clicking "Post Your Answer", you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.