3

I'd like to understand if it's possible to have a sub prototype and optional parameters in it. With prototypes I can do this:

sub some_sub (\@\@\@) {
    ...
}

my @foo = qw/a b c/;
my @bar = qw/1 2 3/;
my @baz = qw/X Y Z/;

some_sub(@foo, @bar, @baz);

which is nice and readable, but the minute I try to do

some_sub(@foo, @bar);

or even

some_sub(@foo, @bar, ());

I get errors:

Not enough arguments for main::some_sub at tablify.pl line 72, near "@bar)"

or

Type of arg 3 to main::some_sub must be array (not stub) at tablify.pl line 72, near "))"

Is it possible to have a prototype and a variable number of arguments? or is something similar achievable via signatures?

I know it could be done by always passing arrayrefs I was wondering if there was another way. After all, TMTOWTDI.

simone
  • 4,667
  • 4
  • 25
  • 47
  • 2
    You can just skip prototypes all together. They are optional. – TLP Mar 31 '21 at 14:39
  • @TLP - my goal is to avoid flattening arrays passed as arguments - see comments in https://stackoverflow.com/questions/8128918/perl-subroutine-prototyping-the-correct-way-to-do-it ( – simone Mar 31 '21 at 14:43

2 Answers2

4

All arguments after a semi-colon are optional:

sub some_sub(\@\@;\@) {
}
Guido Flohr
  • 1,871
  • 15
  • 28
  • Ach! now I found the line in perlsub. And this https://www.effectiveperlprogramming.com/2011/10/understand-why-you-probably-dont-need-prototypes/ and https://perlmonks.org/?node_id=861966 and http://www.modernperlbooks.com/mt/2009/08/the-problem-with-prototypes.html - for future reference – simone Mar 31 '21 at 14:42
  • But, to be fair, what you saw in https://www.effectiveperlprogramming.com/2011/10/understand-why-you-probably-dont-need-prototypes/ was that you shouldn't do this. :) – brian d foy Apr 01 '21 at 10:18
1

Most people are going to expect your argument list to flatten, and you are reaching for an outdated tool to do what people don't expect.

Instead, pass data structures by reference:

some_sub( \@array1, \@array2 );

sub some_sub {
   my @args = @_;
   say "Array 1 has " . $args[0]->@* . " elements";
   }

If you want to use those as named arrays within the sub, you can use ref aliasing

use v5.22;
use experimental qw(ref_aliasing);
sub some_sub {
    \my( @array1 ) = $_[0];
    ...
    }

With v5.26, you can move the reference operator inside the parens:

use v5.26;
use experimental qw(declared_refs);
sub some_sub {
    my( \@array1 ) = $_[0];
    ...
    }

And, remember that v5.20 introduced the :prototype attribute so you can distinguish between prototypes and signatures:

use v5.20;

sub some_sub :prototype(@@;@) { ... }

I write about these things at The Effective Perler (which you already read, I see), in Perl New Features, a little bit in Preparing for Perl 7 (which is mostly about what you need to stop doing in Perl 5 to be future proof).

Alexander Hartmaier
  • 2,178
  • 12
  • 21
brian d foy
  • 129,424
  • 31
  • 207
  • 592