# Arguments and `@_` When a sub is called the classic way (no signature), Perl flattens the entire argument list into a single array and exposes it inside the body as `@_`. The elements of that array are not copies — they are **aliases** to the caller’s expressions. This single rule is the source of most of Perl’s calling-convention surprises and most of its calling-convention power. ## The aliasing rule ```perl sub bump { $_[0]++; # mutates the caller's variable } my $x = 10; bump($x); say $x; # 11 ``` Modifying `$_[0]` writes through the alias to whatever the caller passed. That is by design — it is how `chomp(@lines)` manages to strip newlines from every element of `@lines` rather than from a copy of `@lines`. The aliasing extends to literals, with predictable consequences: ```perl bump(10); # error: Modification of a read-only # value attempted ``` A literal `10` is a constant. The alias points at it; the mutation attempt dies. ## The unpacking idiom Because the aliasing is rarely what the body wants, almost every classic-style sub starts with one of these two patterns: ```perl # Named arguments (most common) sub greet { my ($name, $greeting) = @_; # COPY into private lexicals $greeting //= 'Hello'; return "$greeting, $name!"; } # Method dispatch sub method_call { my $self = shift; # COPY first arg, leave rest in @_ my %args = @_; # remaining args as a hash ... } ``` Once unpacked, the body works with private copies and cannot accidentally mutate the caller’s state. `shift` with no argument inside a sub defaults to `@_`, which is why `my $self = shift;` is everywhere in OO Perl. ## Legitimate uses of aliasing Aliasing is the right tool when you actually want to mutate caller state: ```perl # in-place trim sub trim_inplace { for (@_) { s/^\s+//; s/\s+$//; } } my @data = (" hello ", " world\n"); trim_inplace(@data); # @data is now ("hello", "world") ``` The `for (@_)` loop aliases `$_` to each element of `@_`, which is itself an alias to each element of `@data`. The `s///` modifications go through both alias hops to the original strings. Without the aliasing, this same shape of code would be twice as much work. The same mechanism powers `chomp(@lines)`, `chop(@buf)`, and any «work over a list and modify in place» function you write yourself. State the intent in the function’s name (`_inplace`, `mutate_`, similar) so callers are not surprised. ## Named arguments Perl has no built-in named-parameter syntax in the classic form, but the `=>` (fat comma) plus a hash assignment gives the same effect: ```perl sub configure { my %opt = @_; # ('host' => 'localhost', 'port' => 8080) $opt{host} //= 'localhost'; $opt{port} //= 80; ... } configure( host => 'example.com', port => 8080 ); ``` The fat comma is identical to a regular comma in semantics but quotes the bareword on its left, so `host => ...` is shorthand for `'host' => ...`. See [`,`](../perlop/comma.md) for the operator itself. For mandatory-plus-optional named args, the standard shape is: ```perl sub render { my ($template, %opt) = @_; # one positional, rest as named $opt{escape} //= 1; ... } render('greeting.tt', escape => 0, locale => 'de'); ``` For the modern named-parameter idiom built on signatures, see [signatures](signatures.md). ## `shift`, `pop`, `unshift`, `push` on `@_` `@_` is a real array. You can `shift` from the front, `pop` from the back, slice it, modify it. None of these change the caller’s state — only writing through `$_[N]` does. ```perl sub describe { my $what = shift; # remove first arg from @_ my @rest = @_; # copy what's left ... } sub method { my $self = shift; my @args = @_; $self->dispatch(@args); } ``` A handy consequence: if you want to *re-pack* the remaining args to forward to another sub, just write `@_` — that is exactly what `goto &other` does without the explicit pass. See [recursion](recursion.md) for tail-call forwarding. ## `wantarray` and the calling context Inside a sub, [`wantarray`](../perlfunc/wantarray.md) tells you the context the call was made in: | `wantarray` | Context | Common shape | |---------------|-----------|--------------------------------------------| | `1` (true) | list | return a list | | `0` (false) | scalar | return a single scalar | | `undef` | void | return early; the result will be discarded | ```perl sub items { return unless defined wantarray; # void: nothing to compute my @result = compute_items(); return wantarray ? @result : scalar @result; } ``` `@_` says *what* came in; `wantarray` says *what is expected back*. They are the two halves of the calling-convention picture. See [lvalue and context](lvalue-and-context.md) for the full discussion. ## Common pitfalls - **`my ($x) = @_` vs `my $x = @_`.** The first is list context: `$x` gets the first argument. The second is scalar context: `$x` gets the *count* of arguments. The parentheses on the left are the difference; mistakes here are common. ```perl sub one_arg { my ($x) = @_; # $x = first arg } sub count_args { my $n = @_; # $n = number of args } ``` - **Modifying `$_[N]` «by accident».** Functions like `chomp`, `chop`, `tr///`, `s///` operate on their argument *in place*. `chomp($_[0])` mutates the caller’s string. If that’s not what you want, copy first. - **Flattening of arrays and hashes.** Arrays and hashes passed as arguments lose their identity at the call site — they all become elements of the single flat `@_`. To pass an array without flattening, pass a reference: ```perl takes_array(\@arr); # one ref argument sub takes_array { my ($aref) = @_; push @$aref, 'extra'; } ``` - **`@_` after `goto &sub`.** The current `@_` is passed through to the target. Anything you’ve already shifted off is gone from the perspective of the target. This is a feature, not a bug — see [recursion](recursion.md). ## See also - [`@_`](../perlvar/default.md) — the variable’s perlvar entry, with the aliasing rule and the loop-context discussion. - [`shift`](../perlfunc/shift.md), [`pop`](../perlfunc/pop.md) — default to `@_` inside a sub. - [Signatures](signatures.md) — the modern alternative for named-and-positional parameter handling. - [Return values](return.md) — what the body sends back. - [Comma operator](../perlop/comma.md) — `=>` for named-arg construction.