Scoping

my#

Declare one or more lexically scoped variables.

my introduces new variables whose visibility is bounded by the enclosing block, file, or eval. The compiler records the declaration at compile time; the storage is allocated and (re)initialised each time control enters the scope. Unlike local, my does not temporarily replace a package variable — it creates a genuinely new variable that exists only while the scope is live and is invisible outside it, including to any subroutines the scope calls.

Synopsis#

my EXPR
my TYPE EXPR
my EXPR : ATTRS
my TYPE EXPR : ATTRS

A single variable may be written without parentheses; a list of two or more must be parenthesised:

my $x;
my ($a, $b, @rest);
my Foo $obj;
my $x : shared = 0;

What you get back#

my is a declaration, not a value-producing operator in the ordinary sense. In rvalue position it yields its variable (or the list of variables) as an lvalue you can assign to. The common idioms are:

my $x = $expr;              # scalar context on RHS
my ($first, @rest) = @list; # list context on RHS; destructuring
my @copy = @src;            # list context; @copy gets the elements
my %h = @pairs;             # list context; pairs populate the hash

When no initialiser is given, each declared variable starts as undef (scalar), empty list (array), or empty hash.

Scope and lifetime#

  • Block scope. A my variable is visible from the point immediately after its declaration to the end of the innermost enclosing block, file, eval, do, require, or use’d file.

  • Control-expression scope. The controlling expression of if / unless / elsif / else, while / until, for / foreach is part of the scope of the declaration it contains. In while (my $line = <$fh>) { ... } the variable $line is live throughout the loop body and its continue block, but not beyond.

  • Fresh storage on every entry. Each time control re-enters the scope, the variable is created anew. This is the key difference from state, which preserves its value across entries.

  • Closures capture storage, not values. An anonymous sub created inside the scope keeps that particular instance of the variable alive for as long as the sub is reachable.

sub make_counter {
    my $n = 0;
    return sub { ++$n };    # closes over this $n
}
my $c = make_counter();
$c->(); $c->();             # 1, 2

List form and destructuring#

With parentheses, my supplies list context to the right-hand side and destructures the result:

my ($sec, $min, $hour) = localtime;
my ($head, @tail)      = @list;       # @tail absorbs the rest
my ($x, $y)            = ($y, $x);    # swap via list assignment

Use undef as a placeholder to skip a position:

my (undef, $min, $hour) = localtime;  # discard seconds

Without parentheses, my $foo is a single-variable declaration and the comma operator applies to whatever follows:

my $foo, $bar = 1;          # WRONG: declares $foo only;
                            # $bar is the package variable
my ($foo, $bar) = (0, 1);   # Right.

Context traps#

my does not change how the right-hand side is evaluated — it only modifies the variable on the left. A scalar my $foo imposes scalar context; a parenthesised my (...) imposes list context:

my  $line  = <$fh>;         # scalar context: one line
my ($line) = <$fh>;         # list context: first of a list
my  @lines = <$fh>;         # list context: all remaining lines

The classic mistake is writing my ($foo) = <$fh> when a single line was wanted — the parentheses turn on list context and the filehandle returns a one-element list; the value happens to be the first line, but the surrounding context is wrong for anything that follows.

my vs local vs our vs state#

Four declarations, four different meanings:

  • my — introduces a new lexical variable. Not visible to called code. Fresh storage each entry.

  • local — does not declare anything. It saves the current value of an existing package variable and restores it when the enclosing scope exits. Visible to called code through dynamic scope. Use it for the handful of magical globals ($_, $/, $\, $,, $|, %ENV, …) that cannot be lexicalised.

  • our — declares a lexical alias for a package (global) variable. The storage lives in the package’s symbol table; our just lets you refer to it without the package prefix for the rest of the lexical scope. Satisfies use strict 'vars'.

  • state — like my in visibility, but the storage persists across re-entries to the scope. Initialisation runs once; subsequent entries leave the value alone.

package Counter;
our   $total = 0;           # package global; $Counter::total
my    $cache;               # lexical; invisible outside this file
state $calls = 0;           # lexical; keeps its value across calls
sub tick {
    local $\ = "\n";        # temporarily override output separator
    ++$calls;
    print ++$total;
}

Critical point: my $var does not mask a package variable with the same name. The package variable remains accessible through its fully qualified form while the lexical is in scope:

package main;
our $x = 10;
my  $x = 20;
print "$x and $::x\n";      # "20 and 10\n"

TYPE and attributes#

Both forms are part of the language but their semantics are still evolving (upstream wording).

  • TYPE — a bareword class name, a constant declared via use constant, or __PACKAGE__. Historically bound to the fields pragma; for ordinary use it serves as a hint and does not enforce a type at runtime.

    my Foo $obj = Foo->new;
    
  • ATTRS — a colon-introduced attribute list handled by the attributes pragma (and Attribute::Handlers since 5.8.0).

    my $x : shared;
    my @buf : Locked;
    

See L<perlsub/”Private Variables via my()”> upstream for the authoritative wording.

Declaration is not yet in scope on its own RHS#

The newly declared variable is not visible until after the declaring statement finishes. That makes this idiom well-defined:

my $x = $x;                 # new $x initialised from the old $x

and this condition dependent on a pre-existing outer $x:

my $x = 123 and $x == 123;  # false unless the old $x was 123

Within a single statement, any further mentions of the name refer to the pre-declaration binding (or raise a strict 'vars' error if no such binding exists):

our $x = 2;
foo($x, my $x = $x + 1, $x); # foo() receives (2, 3, 2)

Examples#

Typical subroutine entry — name the arguments:

sub distance {
    my ($x1, $y1, $x2, $y2) = @_;
    return sqrt( ($x2-$x1)**2 + ($y2-$y1)**2 );
}

Loop-local index with for my:

for my $i (1 .. 10) {
    # $i is fresh each iteration; invisible after the loop
}

File-scoped private state — the variable exists for the life of the interpreter but is unreachable from outside this file:

my $cache = {};
sub lookup { $cache->{ $_[0] } //= _compute($_[0]) }

Private subroutine via a lexical coderef:

my $helper = sub { ... };
$helper->($x);

Shadowing warning — redeclaring in the same scope emits a shadow-category warning under use warnings:

use warnings 'shadow';
my $x = 1;
my $x = 2;                  # warning: "my" variable $x masks ...

Edge cases#

  • Only alphanumeric identifiers. Magical built-ins such as $/, $_, $\, %ENV, @ARGV cannot be lexicalised. Use local to scope them dynamically.

  • No package qualification. my $Foo::bar is a syntax error: my variables are not package-owned and cannot carry a :: qualifier.

  • Missing parentheses around a list. my $a, $b declares only $a; $b is the package variable. Always parenthesise two or more names.

  • List-context surprise. my ($line) = <$fh> forces list context on the read; the RHS is a one-element list, not a scalar. Drop the parentheses if you want scalar semantics.

  • foreach without my. for $i (...) localises $i dynamically in the manner of local. Prefer for my $i (...) to confine the index lexically.

  • Shadowing in loops. A my inside a loop body declares a fresh variable every iteration. If that is unwanted (e.g. a counter that should survive iterations), declare it outside the loop or use state.

  • Closure capture. A closure over a my variable keeps that particular instance alive. Different calls to an enclosing sub produce independent closures over independent variables.

  • use strict 'vars' interaction. my satisfies strict-vars within its scope; variables without a declaration must be our, fully qualified, or predeclared with use vars.

  • Compile-time vs run-time. The name binding happens at compile time; the initialiser runs at run time, and re-runs on each scope entry. Expensive initialisers inside hot loops pay that cost every iteration.

Differences from upstream#

Fully compatible with upstream Perl 5.42.

See also#

  • our — lexical alias for a package variable; satisfies strict 'vars' without creating new storage

  • local — dynamically scoped save/restore of an existing package variable; the tool for magical globals

  • state — lexical like my, but storage persists across scope re-entries

  • undef — placeholder in a parenthesised my list, and the initial value of an uninitialised scalar

  • use strict — makes my / our / fully-qualified names mandatory, catching accidental globals

  • attributes — pragma that interprets the : ATTRS portion of a my declaration