Skip to content
Symbol Tables and Glob Magic
step 1/4

Reading — step 1 of 4

Learn

~2 min readIdiomatic and Metaprogramming Perl

Each Perl package has a symbol table — a hash storing all named variables, subs, and filehandles. The package Foo's symbol table is %Foo::. Manipulating it = metaprogramming.

Inspecting:

for my $name (keys %main::) {
    print "$name\n";
}

Prints every global name in main.

Globs — a typeglob *name is a single value containing slots for $name, @name, %name, &name, plus filehandle, format, etc. Assigning to a glob installs into the symbol table.

Generating accessors at runtime:

package Person;
use strict; use warnings;

sub new {
    my ($class, %args) = @_;
    return bless { %args }, $class;
}

for my $attr (qw(name age email)) {
    no strict 'refs';
    *{"Person::$attr"} = sub {
        my $self = shift;
        $self->{$attr} = $_[0] if @_;
        return $self->{$attr};
    };
}

This loop installs three methods (name, age, email) into the Person package — getters and setters in one. Class::Accessor, Moose, and most OO frameworks use this technique.

no strict 'refs' is required because we're indexing the symbol table by string. Re-enable strict immediately after with a block scope.

Aliasing names:

*foo = \&Other::bar;          # foo() now calls Other::bar()
*foo = \@some_array;          # @foo aliases @some_array

B::Deparse for inspecting compiled code:

perl -MO=Deparse -e 'sub greet { print "hi" }'

Shows the parsed-then-printed form. Useful for understanding what Perl turned your code into.

AUTOLOAD — fallback method dispatch:

package SafeHash;

sub new { bless {}, shift }

sub AUTOLOAD {
    my ($self, @args) = @_;
    our $AUTOLOAD;
    my ($method) = $AUTOLOAD =~ /::(\w+)$/;
    return if $method eq 'DESTROY';
    
    if (@args) { $self->{$method} = $args[0]; }
    return $self->{$method};
}

# Usage:
my $h = SafeHash->new;
$h->name("Ada");           # AUTOLOAD intercepts, sets {name}
print $h->name;             # AUTOLOAD intercepts, returns {name}

Caveats:

  • AUTOLOAD runs only if no real method exists
  • Always handle DESTROY (called by GC)
  • $AUTOLOAD holds the full method name being called

Used for: ORM-style row objects, lazy import, missing-method warnings.

UNIVERSAL methods — every blessed object inherits these:

  • $obj->isa('SomeClass') — true if $obj is/inherits from SomeClass
  • $obj->can('method') — returns coderef if method exists, else undef
  • $obj->DOES('Role') — Moose/role-aware isa
if ($obj->can('serialize')) {
    $obj->serialize();
}

Closures over loop variables — classic gotcha:

my @subs;
for my $i (1..3) {
    push @subs, sub { return $i };    # captures $i FRESH per iteration (good)
}
print $subs[0]->();   # 1

In Perl this works correctly — my $i is scoped to each iteration. (In old for (my $i = 0; ...; ...) C-style loops, beware: $i is shared across iterations.)

Perl metaprogramming is powerful, occasionally dangerous. Frameworks like Moose hide most of it behind ergonomic APIs.

Discussion

Ask a question, share an insight, or help someone who’s stuck.

Sign in to post a comment or reply.

Loading…