60

I have been reading about Perl recently and am slightly perplexed about how Perl handles arguments passed to subroutines.

In a language like Python, Java or PHP, a function definition takes the form (in pseudocode):

function myFunc(arg1, arg2) {
    // Do something with arg1 and arg2 here
}

Yet in Perl it's just:

sub mySub {
    # @_ holds all arguments passed
}

And as I understand it, that's the only way to do it.

  • What if I want to restrict the caller to only pass two arguments?

  • Isn't this just Perl not allowing anything but variable-number arguments in other languages (i.e., Python, C, etc.)?

  • Wouldn't that become a problem at some point?

  • What about all the default argument-number checking in other languages? Would one have to do that explicitly in Perl? For instance

      sub a_sub {
          if (@_ == 2) {
              # Continue function
          }
          else {
              return false
          }
      }
    
Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
Aristides
  • 3,805
  • 7
  • 34
  • 41
  • 8
    There are function prototypes. There are also good reasons not to use function prototypes. See also [Perl Subroutine Prototyping -- The correct way to do it](http://stackoverflow.com/questions/8128918/perl-subroutine-prototyping-the-correct-way-to-do-it). It references [Far more than you ever wanted to know about prototypes in Perl](http://www.perlmonks.org/?node_id=861966) which you should read before you go much further. – Jonathan Leffler Oct 07 '13 at 20:38
  • 1
    `length(@_)` is one of those things that does not do what you think it does. You want just `@_` (or `scalar @_` if you want to be pedantic). Otherwise, you'll get the number of digits in the number of arguments. – cjm Oct 07 '13 at 20:43
  • @cjm whatever, it was just to demonstrate the point of needing to explicitly check number of args. – Aristides Oct 07 '13 at 20:45
  • 1
    [`perldoc perlsub`](http://perldoc.perl.org/perlsub.html) – pilcrow Oct 07 '13 at 20:59
  • 24
    Oh, the beauty of Perl! Perl was written by a linguist, who knew that human languages are not strongly typed and have no fixed argument lists, so he did not impose these brain damages on Perl users. Welcome to freedom, baby! – SzG Oct 07 '13 at 21:29
  • 1
    "Isn't this just Perl not allowing anything but variable-number arguments in other languages". Now try this in both JavaScript and PHP: `function f($x) {}; f(1,2,3,4,5,6,7,8,9,10); f();` - runs with no problems. And people say that Perl is weird... – el.pescado - нет войне May 05 '16 at 10:51
  • 1
    @SzG Why is [Larry Wall](https://en.wikipedia.org/wiki/Larry_Wall) so cool. – David Tonhofer Mar 28 '20 at 10:02
  • Related (but that is for the command-line): *[How can I pass command-line arguments to a Perl program?](https://stackoverflow.com/questions/361752/how-can-i-pass-command-line-arguments-to-a-perl-program)* – Peter Mortensen Oct 06 '22 at 11:02

7 Answers7

92

You are wary of the Perl environment because it is quite different from the languages you have come across before.

The people who believe in strong typing and function prototypes will disagree here, but I believe that restrictions like that are rarely useful. Has C really caught you passing the wrong number of parameters to a function often enough to be useful?

It is most common in modern Perl to copy the contents of @_ to a list of lexical scalar variables, so you will often see subroutines starting with

sub mysub {
  my ($p1, $p2) = @_;
  ... etc.
}

that way, all parameters that are passed will be available as elements of @_ ($_[0], $_[1] etc.) while the expected ones are named and appear in $p1 and $p2 (although I hope you understand that those names should be chosen appropriately).

In the particular case that the subroutine is a method, the first parameter is special. In other languages it is self or this, but in Perl it is simply the first parameter in @_ and you may call it what you like. In those circumstances you would see

sub method {
  my $self = shift;
  my ($p1, $p2) = @_;
  ... etc.
}

so that the context object (or the name of the class if it is a class method) is extracted into $self (a name assumed by convention) and the rest of the parameters remain in @_ to be accessed either directly or, more usually, copied to local scalar variables as $p1, $p2 etc.

Most often the complaint is that there is no type checking either, so I can pass any scalar I like as a subroutine parameter. As long as use strict and use warnings are in context, even this is generally simple to debug, simply because the operations that the subroutine can perform on one form of scalar are usually illegal on another.

Although it was originally more to do with encapsulation with respect to object-oriented Perl, this quote from Larry Wall is very relevant

Perl doesn't have an infatuation with enforced privacy. It would prefer that you stayed out of its living room because you weren't invited, not because it has a shotgun

C was designed and implemented in the days when it was a major efficiency boost if you could get a faulty program to fail during compilation rather than at run time. That has changed now, although a similar situation has arisen with client-side JavaScript where it actually would be useful to know that the code is wrong before fetching the data from the internet that it has to deal with. Sadly, JavaScript parameter checking is now looser than it should be.


Update

For those who doubt the usefulness of Perl for teaching purposes, I suggest that it is precisely because Perl's mechanisms are so simple and direct that they are ideal for such purposes.

  • When you call a Perl subroutine all of the parameters in the call are aliased in @_. You can use them directly to affect the actual parameters, or copy them to prevent external action

  • If you call a Perl subroutine as a method then the calling object or class is provided as the first parameter. Again, the subroutine (method) can do what it likes with @_

Borodin
  • 126,100
  • 9
  • 70
  • 144
  • 6
    @Borodin: You probably got the critic of "strong typing and function prototypes" wrong. The point of being able to use typed arguments and declared arguments is that the compiler is able to do compile-time checks and optimizations on them (as the other languages do). Larry explicitly left this door open until this possibility is implemented, but p5p is so far strictly against such compiler optimizations. Properly implemented argument handling led to run-time improvements >10x faster in external compilers, but when p5p was evaluating this path it choose to go the slow way and always copy @_. – rurban Sep 17 '14 at 19:24
  • @rurban: I presume you mean the *advocates* of strong typing etc? And yes, there is some advantage *for the compiler* in prescribing the exact type of every variable and parameter, but in practice the problem is one of programming expediency. Just as an occasional segment of a C program must be rewritten in assembler, so it is sometimes best to resort to a C (XS) module in Perl. The overridingly expensive commodity is programmer time, and that must be minimised over all other costs to reach the performance goal. The pressure is on the designer. – Borodin Oct 04 '14 at 08:21
  • 1
    -1. prototypes not mentioned, says 'strong' instead of 'static' typing (they are very different things), fundamentally misrepresents the purpose of static typing (it's not about turnaround time: 1: runtime checking only occurs if the code path is followed--thus extensive testing is a necessity in languages that aren't statically typed, and 2: runtime checking is expensive). And yes, omitting arguments to a function call happens often enough to make that checking worthwhile (and because Perl doesn't, it requires an undef value and the expense of checking it at runtime). Different design goals. – Jim Balter Jul 20 '19 at 20:24
  • I have to manually add a code guard to every function to check whether excess arguments have been passed or not. How can anyone call this an advantage? – Jeyekomon Feb 08 '23 at 07:44
24

Perl doesn't manage your argument handling for you. Instead, it provides a minimal, flexible abstraction and allows you to write code that fits your needs.

Pass By Reference

By default, Perl sticks an alias to each argument in @_. This implements basic, pass by reference semantics.

my $num = 1;
foo($num);
print "$num\n";  # prints 2.

sub foo { $_[0]++ }

Pass by reference is fast but has the risk of leaking changes to parameter data.

Pass By Copy

If you want pass by copy semantics, you need to make the copies yourself. Two main approaches to handling lists of positional parameters are common in the Perl community:

sub shifty {
    my $foo = shift;
}

sub listy {
    my ($foo) = @_;
}

At my place of employment we do a version of listy:

sub fancy_listy {

    my ($positional, $args, @bad) = @_;

    die "Extra args" if @bad;
}

Named Parameters

Another common practice is the use of named parameters:

sub named_params {
    my %opt = @_;
}

Some people are happy with just the above. I prefer a more verbose approach:

sub named_params {
    my %opt = @_;

    my $named = delete $opt{named} // "default value";
    my $param = delete $opt{param}
        or croak "Missing required 'param'";

    croak "Unknown params:", join ", ", keys %opt
        if %opt;

    # do stuff 
}

This unpacks named params into variables, allows space for basic validation and default values and enforces that no extra, unknown arguments were passed in.

On Perl Prototypes

Perl's "prototypes" are not prototypes in the normal sense. They only provide compiler hints that allow you to skip parenthesis on function calls. The only reasonable use is to mimic the behavior of built-in functions. You can easily defeat prototype argument checking. In general, DO NOT USE PROTOTYPES. Use them with with care that you would use operator overloading--i.e. sparingly and only to improve readability.

daotoad
  • 26,689
  • 7
  • 59
  • 100
  • a little oddity that's worth mentioning: You're not allowed to change hash keys, so if you have a sub like `sub a {$_[0]=2}` and call it with a hash it will silently ignore the assignment. – Patrick J. S. Oct 04 '14 at 20:49
  • @Borodin: yes, `a(%h)` is what I meant. I find it worth mentioning, because, while maybe being an example for perl ignorance, it is an example where unexpected things could happen: Think of a (improperly implemented) `sub` with parametric arguments where you want to make sure that all arguments are the same case. `sub a{$_=lc$_`for@_;%opts=@_;...} a(%h);`. While perl does the intended thing (copying the hash keys inside the subroutine, not changing the hash), the unwary might be suprised by this rarely documented behaviour. – Patrick J. S. Oct 20 '14 at 14:48
8

For some reason, Perl likes lists, and dislikes static typing. The @_ array actually opens up a lot of flexibility, because subroutine arguments are passed by reference, and not by value. For example, this allows us to do out-arguments:

my $x = 40;
add_to($x, 2);
print "$x\n"; # 42

sub add_to { $_[0] += $_[1] }

… but this is more of an historic performance hack. Usually, the arguments are “declared” by a list assignment:

sub some_sub {
  my ($foo, $bar) = @_;
  #               ^-- this assignment performs a copy
  ...
}

This makes the semantics of this sub call-by-value, which is usually more desirable. Yes, unused arguments are simply forgotten, and too few arguments do not raise any automatic error – the variables just contain undef. You can add arbitrary validation e.g. by checking the size of @_.


There exist plans to finally make named parameters available in the future, which would look like

sub some_sub($foo, $bar) { ... }

You can have this syntax today if you install the signatures module. But there is something even better: I can strongly recommend Function::Parameters, which allows syntax like

fun some_sub($foo, $bar = "default value") { ... }

method some_method($foo, $bar, :$named_parameter, :$named_with_default = 42) {
  # $self is autodeclared in methods
}

This also supports experimental type checks.

Parser extensions FTW!

RobEarl
  • 7,862
  • 6
  • 35
  • 50
amon
  • 57,091
  • 2
  • 89
  • 149
  • How do you reconcile *"to few arguments are not detectable in any way"* and the immediately *"You can add ... validation e.g. by checking the size of @_"*? – Borodin Oct 07 '13 at 22:44
  • I also worry about the wisdom of recommending either `signatures` or `Function::Parameters`, which may scratch an itch but will have most other Perl programmers scratching their heads. There is little wrong with the `my ($p1, $p2, $p3) = @_` idiom, preceded by `my $self = shift` if the subroutine is a method. – Borodin Oct 07 '13 at 22:47
  • 1
    @Borodin Yes, your first comment does point out a bad formulation. To the 2nd: I have nothing against the simple list assignment except that it is excessively verbose. To a non-Perler, modules like F:P can provide a bridge to Perl by removing historical oddities. It also makes using named params much easier. For personal code, I use syntax extensions all the time, because they make Perl more expressive. – amon Oct 07 '13 at 23:10
6

If you really want to impose stricter parameter checks in Perl, you could look at something like Params::Validate.

Dave Cross
  • 68,119
  • 3
  • 51
  • 97
5

Perl does have the prototyping capability for parameter placeholders, that you're kind of used to seeing, but it's often unnecessary.

sub foo($){
    say shift;
}; 
foo();      # Error: Not enough arguments for main::foo
foo('bar'); # executes correctly

And if you did sub foo($$){...} it would require 2 non-optional arguments (eg foo('bar','baz'))

vol7ron
  • 40,809
  • 21
  • 119
  • 172
  • 5
    It's counterproductive to recommend prototypes to a new Perl programmer. They are too unlike prototypes in other languages and don't really provide actual security so much as a false sense of security. – mob Oct 07 '13 at 21:33
  • `And as I understand it, that's the only way to do it. What if I want to restrict the caller to only pass 2 arguments?` It sounds like the OP knows what he's talking about, new to Perl or not. The other answers aren't about restricting to 2 arguments, they're only about popping 2 off the array stack and evaluating that. But, you're right about the last part, but that applies to a lot of Perl. – vol7ron Oct 07 '13 at 21:41
5

If you are reading about Perl recently, please read about recent Perl. You may read the Modern Perl book for free as well.

Indeed, while with old standard Perl you would need to restrict the number of arguments passed to a subroutine manually, for example with something like this:

sub greet_one {
    die "Too many arguments for subroutine" unless @_ <= 1;
    my $name = $_[0] || "Bruce";
    say "Hello, $name!";
}

With modern Perl you can take advantage of function signatures. Here are a few examples from the Modern Perl book:

sub greet_one($name = 'Bruce') {
    say "Hello, $name!";
}

sub greet_all($leader, @everyone) {
    say "Hello, $leader!";
    say "Hi also, $_." for @everyone;
}

sub make_nested_hash($name, %pairs) {
    return { $name => \%pairs };
}

Please, note that function signatures were introduced in Perl 5.20 and considered experimental until Perl 5.36. Therefore, if you use a Perl version in that range you may want to disable warnings for the "experimental::signatures" category:

use feature 'signatures';
no warnings 'experimental::signatures';
Paolo Rovelli
  • 9,396
  • 2
  • 58
  • 37
Sachin Dangol
  • 504
  • 5
  • 13
3

You can just use:

my ($arg1, $arg2) = @_;

To explicitly limit the number of arguments you can use:

my $number =2;
die "Too many arguments" if @_ > $number;
fugu
  • 6,417
  • 5
  • 40
  • 75
  • 2
    And that would discard the rest of the arguments? – Aristides Oct 07 '13 at 20:39
  • 1
    Yes; it would not prevent extra arguments being passed -- it just means the function will ignore them (unless it accesses `@_` directly). – Jonathan Leffler Oct 07 '13 at 20:39
  • 2
    This isn't unique among languages; JavaScript, at the very least, also silently accepts extra arguments (they don't get names, but they're available in `arguments`). – hobbs Oct 07 '13 at 20:42
  • @hobbs Oh, I wasn't aware of that. I guess I am just used to explicitly spelling out the arguments a function accepts like in C et al. – Aristides Oct 07 '13 at 20:44
  • @Aristides compiled languages often do this for memory allocation purposes – vol7ron Oct 07 '13 at 21:17
  • Python, Ruby, etc. also do that, and they're interpreted. – Aristides Oct 07 '13 at 21:18
  • 2
    This seems to fall a long way short of answering the questions in the original post. – Borodin Oct 07 '13 at 22:27
  • The question was *"What if I want to restrict the caller to only pass two arguments?"*. That is, two and only two arguments (not at most two arguments). This will not catch if the function is called with ***one*** argument. Can you update your answer? ***Without*** "Edit:", "Update:", or similar - the answer should appear as if it was written today – Peter Mortensen May 01 '21 at 21:08