sub add1 {
my ($arg1, $arg2) = @_;
$arg1 + $arg2;
}
sub add2 {
my $arg1 = shift;
# possibly two screens of dense code here, then
my $arg2 = shift;
$arg1 + $arg2;
}
sub add3 {
$_[0] + $_[1]
}
# There may be some other ways to extract arguments
# that I am not aware of.
# It's possible to combine any of the methods!
And this is just argument access, one of the core language primitives. def add1(*_):
_ = list(_)
arg1, arg2 = _
return arg1 + arg2
def add2(*_):
_ = list(_)
arg1 = _.pop(0)
arg2 = _.pop(0)
return arg1 + arg2
def add3(*_):
_ = list(_)
return _[0] + _[1] def add(x, y):
return x + y
whereas the Perl codebase that I work with has every possible combination of these methods.e: Just clarifying that your complaint here is that every sub in Perl 5 just receives a list of its arguments; this isn't a "more than one way to do it" issue.
The old P5 motto TIMTOWTDI was long ago updated to TIMTOWTDIBSCINABTE and P6 adopts the latter. While P6 supports most of the options shown for P5, most folk writing P6 will just write something like:
sub add($x, $y) { $x + $y }How much actual cognitive overhead is there associated with three of four different ways of unpacking args?
Speaking for myself (as a long-time Perl/Python/GoLang/etc programmer), when I jump into a language I don't know, it doesn't take me long to get 'muscle memory' for how such things work.
It doesn't matter which way you unpack your args unless you are trying to do something fancy (the sub 1% cases). Just pick one.
Hell, it's easy enough to make a script to rewrite multiple shifts into destructured list assignment or vice versa.
Because in the second (add2), you might do something interesting with the second (or third) arguments depending on the first. You might have optional arguments. Your arguments might be a list of items you don't know the size of. It allows you to develop what you need out of the extensible core mechanism. Modules can and have built on that.
> # There may be some other ways to extract arguments
There are. Now you can just do
sub add( $arg1, $arg2 ) {
$arg1 + $arg2;
}
My personal favorite is to use Function::Parameters[1] fun add( Num $arg1, Num $arg2 ) {
$arg1 + $arg2;
}
which allows named params, type constraints (runtime), default params, etc.The first example uses the explicitly named default array @_. This is a common pattern, and easy to read.
The second one omits the "default". Note that whilst it is possible to write some dense code between the $arg1 and $arg2, it won't work as expected if the dense code bit has array access - ie., the default array can change in the code.
The third example uses the typical sigils for accessing individual elements within an array (default or otherwise).
I wish the core language had saner defaults, but over time, I've seen some reasonable uses for the different styles:
1. General subs, the same as you example
2. shift removes the first element of the array and returns it. This can make the code more readable in certain cases:
use Params::Validate 'validate';
sub add2 {
my $self = shift;
my $args = validate (@_, { ... } );
}
3. Slightly less verbose code, for simple one-liners and/or anonymous functions: my $calc_functions = {
'add' => sub { $_[0] + $_[1] },
'subtract' => sub { $_[0] - $_[1] },
...
};
my $func = 'add';
$calc_functions->{$func}->($args);add3 shows how to access the aliased arguments directly.
# break the aliasing of @_ and make undef into 0.
sub undef_to_zero { @_ = map { $_ // 0 } @_; }
sub math_stuff {
# call with identical @_
&undef_to_zero;
# Do math without undef warnings.
my $sum = 0;
$sum += $_ for @_;
}
# Alter all arguments in calls by repeating them
sub fatten {
$_ = "$_" x 2 for @_;
}
$foo = "abc";
@bar = ( 1, 2, 4 );
foo($foo, @bar);
say $foo; # abcabc
say for @bar # 11
# 22
# 44