Skip to content
Lesson 6 of 7

Step 1 of 4 · Reading · ~2 min

Learn

Idiomatic 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.

Up nextPragmas, Tied Variables, and Effective PerlIdiomatic and Metaprogramming Perl

Discussion

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

Sign in to post a comment or reply.

Loading…