Skip to content
Dispatch Tables and Functional Patterns
step 1/5

Reading — step 1 of 5

Learn

~3 min readIdiomatic and Metaprogramming Perl

Long if/elsif chains are an anti-pattern in any language. In Perl, the idiomatic replacement is a dispatch table — a hash whose values are coderefs.

Basic dispatch table:

use strict; use warnings;

my %ops = (
    add => sub { $_[0] + $_[1] },
    sub => sub { $_[0] - $_[1] },
    mul => sub { $_[0] * $_[1] },
    div => sub { $_[1] != 0 ? $_[0] / $_[1] : die "div zero" },
);

my ($op, $a, $b) = ('add', 3, 4);
my $result = $ops{$op}->($a, $b);
print "$result\n";   # 7

Hash lookup is O(1). The structure is data, not control flow — easy to extend at runtime.

Command parser — common in CLIs and protocol handlers:

my %commands = (
    help    => \&do_help,
    version => \&do_version,
    list    => \&do_list,
    add     => \&do_add,
);

my ($cmd, @args) = @ARGV;
$commands{$cmd} ? $commands{$cmd}->(@args) : usage();

Adding a new command = adding a hash entry. No if/elsif maintenance.

Method dispatch on data attribute:

sub handle {
    my ($event) = @_;
    my %handler = (
        login  => sub { ... },
        logout => sub { ... },
        click  => sub { ... },
    );
    if (my $h = $handler{$event->{type}}) {
        return $h->($event);
    }
    warn "unknown event type: $event->{type}";
}

Closures inside dispatch tables — shared state:

sub make_calculator {
    my $value = 0;
    return {
        add   => sub { $value += $_[0] },
        reset => sub { $value = 0 },
        get   => sub { $value },
    };
}

my $calc = make_calculator();
$calc->{add}->(10);
$calc->{add}->(5);
print $calc->{get}->(), "\n";   # 15

This is the Perl version of an object — without bless. Sometimes nicer because the interface is just a hash; very transparent.

map / grep / sort are first-class FP:

use List::Util qw(reduce);

my @nums = (1, 2, 3, 4, 5);

my $sum_squares = reduce { $a + $b } map { $_ ** 2 } @nums;
my @evens_doubled = map { $_ * 2 } grep { $_ % 2 == 0 } @nums;

Note: in reduce, $a and $b are accumulator and current item.

Schwartzian transform — Perl's classic decorate-sort-undecorate idiom for expensive sort keys:

# sort filenames by mtime, expensive to call -M repeatedly
my @sorted = map  { $_->[1] }                # 3. extract original
             sort { $a->[0] <=> $b->[0] }    # 2. sort by precomputed
             map  { [-M $_, $_] }             # 1. decorate with key
             @files;

Read bottom-up: decorate, sort, undecorate. Each -M runs once per file instead of N log N times.

Composition with closures:

sub compose {
    my @fns = @_;
    return sub {
        my @args = @_;
        for my $f (reverse @fns) {
            @args = $f->(@args);
        }
        return wantarray ? @args : $args[0];
    };
}

my $f = compose(
    sub { $_[0] + 1 },
    sub { $_[0] * 2 },
);
print $f->(5);   # (5*2)+1 = 11

Functional patterns blend smoothly with Perl's procedural roots — use them where they make code clearer.

Discussion

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

Sign in to post a comment or reply.

Loading…