To be a bit pedantic, these are not different ways to get a sub arguments, but accessing an array in different ways ("default array" in this case). If I understand this correctly, your criticism is aimed at how the core language makes arguments available to a sub body, in the default array.
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);