Perl — Reference

Source: https://perldoc.perl.org/

Perl

  • Created: 1987 by Larry Wall
  • Latest stable: 5.42.2 (2025-11; security/maintenance release of the 5.42 series). Perl 5 is the production language; “Raku” (renamed from Perl 6 in 2019) is a separate language with a separate community.
  • Paradigms: Multi-paradigm — procedural, functional, object-oriented (multiple OO systems), reflective. Class syntax (experimental, stabilizing) since 5.38.
  • Typing: Dynamic, weak-ish (sigils distinguish scalars $, arrays @, hashes %); duck-typed objects.
  • Memory: Reference-counted (with cycle detection via weak refs); no compacting GC.
  • Compilation: Source compiled to an internal opcode tree at startup, then walked by the Perl interpreter. No standalone binaries by default.
  • Primary domains: Sysadmin scripting, text/regex processing, bioinformatics (BioPerl), CPAN-driven backend services, log/ETL pipelines, legacy CGI/web (Mojolicious, Dancer2, Catalyst).
  • Notable implementations: perl5 (the canonical interpreter); cperl (fork, dormant). Raku/Rakudo is a separate language.
  • Official docs: https://perldoc.perl.org/

At a glance

Perl 5 is a battle-hardened scripting language with deep regex roots, the largest pre-modern package archive (CPAN, ~200k modules), and unmatched text-munging ergonomics. Sigils on every variable, contextual evaluation (list vs scalar), and TIMTOWTDI (“there’s more than one way to do it”) shape its style. The “modern Perl” wave (post-5.10) brought say, state, //, smartmatch, then feature 'signatures', postfix dereference, and feature 'class' (5.38+) for native OO without Moose.

Getting started

Install — Most Linux distros ship perl. For an isolated, modern build use perlbrew (https://perlbrew.pl/) or Plenv:

\\wsl$ curl -L https://install.perlbrew.pl | bash
perlbrew install perl-5.42.2 && perlbrew switch perl-5.42.2

Windows: Strawberry Perl (https://strawberryperl.com/) bundles a compiler for XS.

Hello world (hello.pl):

use v5.42;             # enables strict, warnings, signatures, say
say "Hello, world!";

Run: perl hello.pl.

Project layout (CPAN-style with Module::Build / ExtUtils::MakeMaker):

My-App/
  lib/My/App.pm
  t/00-load.t           # tests
  cpanfile              # deps (Carton/cpm)
  Makefile.PL or Build.PL or dist.ini (Dist::Zilla)

Package managercpanm (App::cpanminus) for installs, Carton or Carmel for lockfiles, cpm (faster). CPAN itself: https://www.cpan.org/. Modern alternative: App::cpm (parallel installs).

REPLperl -de 1 (debugger as REPL), or Reply (cpanm Reply), or re.pl from Devel::REPL.

Basics

Sigils & literals$scalar (number, string, ref), @array, %hash. Numbers: 42, 3.14, 0xff, 0b1010, 1_000_000. Strings: 'literal' (no interpolation), "$var interpolated". Heredoc: <<~END ... END (5.26+ indented). Regex literal: qr/pattern/.

Variables/scopingmy $x (lexical, scoped to enclosing block), our $x (package global alias), state $x (persistent within sub, since 5.10), local $x (dynamic scope on package globals — for $_, $/, etc.). use strict; use warnings; is mandatory in modern code (or use v5.36;+ which enables both).

Control flowif/elsif/else, unless, while, until, for/foreach, last/next/redo, postfix forms (print $x if $cond). Pattern matching: given/when is deprecated; use dispatch tables or match::simple. Ternary ?:. Defined-or // and //=.

Functions — Subroutines via sub. Modern signatures (5.36+ stable):

use v5.36;
sub greet ($name = 'world', @rest) {
    return "Hello, $name; extras: @rest";
}

Closures and first-class subs:

my $adder = sub ($x) { sub ($y) { $x + $y } };
say $adder->(2)->(3);   # 5

Old-school args via @_. Prototypes (sub foo($$@) { }) are advisory and largely superseded by signatures.

Strings & interpolation — Double-quoted strings interpolate scalars and arrays ("@list" joins on $, then $"). sprintf like C. qq{}, q{}, qw{a b c} (word list).

Collections — Arrays @a = (1, 2, 3), indexed $a[0]. Hashes %h = (a => 1, b => 2), accessed $h{a}. References: \@arr, \%h, \$x; deref @$aref, $$aref[0] or $aref->[0]. Postfix deref (5.24+): $aref->@*, $href->%*.

Intermediate

Type system — No static types in core. Type::Tiny (https://metacpan.org/pod/Type::Tiny) is the de-facto type library used by Moose/Moo/Mojo for runtime constraints + coercions.

Modules/packages — One package per file by convention; package My::Foo; declares namespace. use My::Foo; loads + imports. Exporter (@EXPORT_OK, %EXPORT_TAGS) handles symbol export. our @ISA for inheritance (or use parent 'Base').

Error handlingdie "msg\n" (or die { code => 1, msg => ... } for object), caught by eval { ... }; if ($@) { ... }. Modern: Try::Tiny, Syntax::Keyword::Try (since 5.34, native use feature 'try' provides try/catch/finallyfinally stable in 5.36). Carp (croak, confess, cluck) reports from caller’s perspective.

Concurrencyfork (heavyweight, OS-level — Perl is copy-on-write friendly), Parallel::ForkManager, IPC::Run. threads (ithreads — interpreter-cloning, slow startup; mostly deprecated). Async I/O: AnyEvent, IO::Async, Mojo::IOLoop, Future::AsyncAwait (use Future::AsyncAwait; async sub { ... await $f; }). Coroutines: Coro (deprecated XS). Most modern Perl is single-threaded event-driven.

File I/O & networkingopen my $fh, '<:encoding(UTF-8)', $path or die ...; while (<$fh>) { ... }. Path::Tiny for path manipulation (the modern replacement for File::Spec + File::Path + File::Find). Networking: IO::Socket::IP, Net::SSH2, LWP::UserAgent / HTTP::Tiny / Mojo::UserAgent for HTTP.

Stdlib highlightsList::Util / List::MoreUtils / List::AllUtils, Scalar::Util (weaken, blessed, reftype), Data::Dumper, JSON::PP (core)/Cpanel::JSON::XS (fast), Time::Piece, DateTime, File::Find, Getopt::Long, Pod::Usage, Storable, Encode, utf8 pragma.

Advanced

Memory model & GC — Reference counting on every SV (Scalar Value). Cycles leak unless broken with Scalar::Util::weaken($ref). Devel::Cycle, Devel::Leak, Devel::Gladiator, Devel::MAT for leak hunting. Memory is process-local — fork inheritance is COW-friendly (Apache/mod_perl, Starman preforks).

Concurrency deep diveIO::Async (Paul Evans) is the modern event framework with first-class Futures; integrates with Future::AsyncAwait for native async/await. Mojo::IOLoop (libev-based). Net::Async::HTTP, Net::Async::Beanstalk etc. Multi-process: Parallel::ForkManager, MCE (Many-Core Engine) for shared-memory fork pools.

FFI/interop — Two paths: XS (legacy, the canonical C glue — .xs files compiled with ExtUtils::MakeMaker, full access to internals); FFI::Platypus (https://metacpan.org/pod/FFI::Platypus) — modern libffi-based, no C compiler needed at install time, much friendlier:

use FFI::Platypus 2.00;
my $ffi = FFI::Platypus->new(api => 2);
$ffi->lib(undef);                        # libc
$ffi->attach(puts => ['string'] => 'int');
puts("Hello from libc");

Inline::C, Inline::CPP for inline C/C++ snippets at compile time.

Reflectionref($x), Scalar::Util::blessed, Scalar::Util::reftype, package introspection via Symbol, B::* modules walk the optree. MRO::Compat / mro::c3 for method-resolution inspection. Moose::Util::TypeConstraints and Moose::Meta::Class for full MOP.

Performance toolsDevel::NYTProf (the gold-standard line/sub profiler with HTML reports), Devel::DProf (older), Devel::Cover (coverage). Benchmark micro: Benchmark, Dumbbench. Profile memory with Devel::SizeMe. The opcode tree: B::Concise -exec to dump the optree of any sub.

God mode

Tied variablestie $scalar, 'My::Tie::Class', @args re-routes every read/write through FETCH/STORE. Tie::File makes a file look like an array; Tie::Hash::DotNotation flattens nested hashes. UNTIE to detach. The mechanism powers %ENV, %SIG, DBM files.

Source filtersFilter::Util::Call lets a module rewrite source as it’s read. Filter::Simple is the friendlier wrapper. Real uses: Switch.pm (historical), Acme::Bleach (joke), Smart::Comments (debug-only ### $var). Plays nice with # line directives so error messages still point at the original.

B::Deparse / B::Conciseperl -MO=Deparse -e 'my $x = 1 + 2;' recovers (a normalized form of) the source from the optree — invaluable for inspecting macros and prototypes. perl -MO=Concise,foo myscript.pl dumps the opcodes for sub foo (Perl’s “disassembly”).

Devel::PeekDump($scalar) prints the SV’s flags, refcount, IV/NV/PV slots, magic chain. Essential for debugging tied vars, blessed refs, internal encoding (SVf_UTF8).

AUTOLOAD — Define sub AUTOLOAD { ... $AUTOLOAD ... } in a package; missing-method calls dispatch to it. The classic dynamic-proxy mechanism — used by Class::DBI, DBIx::Class for column accessors, Net::Telnet::Cisco for command bindings.

Prototypes & signatures — Old-style prototypes (sub mymap (&@)) influence parse-time argument compilation (the magic behind map BLOCK LIST); use prototype() to inspect, Scalar::Util::set_prototype to set. Modern use feature 'signatures' (default in use v5.36) is a separate, parse-time mechanism.

MOP via Moose / Moo / Object::PadMoose (https://metacpan.org/pod/Moose) is the heavyweight Class::MOP-based metaobject protocol — define classes with has, roles via with, type constraints, method modifiers (before/after/around), introspection via Moose::Meta::Class. Moo (https://metacpan.org/pod/Moo) is the lite, no-XS, lazy-loading variant (upgrades to Moose if loaded). Object::Pad is Paul Evans’ research vehicle that became the basis of the new core class feature.

Native class syntax (5.38+) — Experimental but stabilizing. Real lexical fields, method declaration, ADJUST blocks:

use v5.38;
use feature 'class'; no warnings 'experimental::class';
class Point {
    field $x :param = 0;
    field $y :param = 0;
    method distance ($other) { sqrt(($x - $other->x)**2 + ($y - $other->y)**2) }
    method x { $x }  method y { $y }
}

See perlclass. Inheritance via :isa(Base) (5.38+). Roles still via Moose/Role::Tiny pending core work.

Perl XS (C extensions).xs files declare a typemap-driven C-to-Perl glue. h2xs scaffolds. Used by performance-critical CPAN modules (JSON::XS, Cpanel::JSON::XS, Sereal, Mojolicious::Plugin::*). API in perlxs, perlguts, perlapi.

Embedding the perl interpreterEXTERN_C void perl_construct(PerlInterpreter*); from C — pass argv, call perl_run. Used by mod_perl (Apache), nginx-perl, embedded controllers. Multiple interpreters via MULTIPLICITY build flag.

Opcodes via B::* — B, B::Concise, B::Deparse, B::Xref walk the parsed tree. Optimization plays at this level; optimizer and B::Hooks::OP::Annotation let modules rewrite ops at compile time.

Idioms & style

  • Naming: snake_case for subs and variables; CamelCase for packages/modules; UPPER_SNAKE for constants. File My/Foo/Bar.pm ↔ package My::Foo::Bar.
  • use strict; use warnings; non-negotiable. use v5.36; enables both plus signatures and say.
  • Formatter: perltidy (the canonical formatter; .perltidyrc).
  • Linter: Perl::Critic (perlcritic) with policy levels (gentle/stern/harsh/cruel/brutal). PBP (Perl Best Practices, Damian Conway, 2005) drives the default policies.
  • Idiomatic: list-context comprehension via map/grep; unless/until instead of negated if/while (sometimes contentious); Path::Tiny over File::Spec; Try::Tiny/native try over bare eval { }; Moo/Moose for non-trivial OO until core class matures; CPAN-first (“there’s a module for that”).
  • Reviewers look for: missing use strict/warnings; unbounded slurp of large files; eval { } without checking $@; cycles (missing weaken); shelling out without system { LIST } form (security); regex without use re 'strict' for production.

Ecosystem

  • Web: Mojolicious (real-time, batteries-included, https://mojolicious.org/), Dancer2 (Sinatra-style), Catalyst (MVC, mature), PSGI/Plack (the WSGI/Rack equivalent — common substrate). App servers: Starman, Hypnotoad, Twiggy, uWSGI.
  • DB/ORM: DBI (universal driver layer), DBD::Pg/mysql/SQLite, DBIx::Class (heavyweight ORM), DBIx::Class::Schema::Loader, Rose::DB::Object, Mojo::SQLite/Pg/mysql for migrations.
  • Async: IO::Async, AnyEvent, Mojo::IOLoop, POE (legacy).
  • Testing: Test2::Suite (the modern stack), Test::More (still ubiquitous), Test::Deep, Devel::Cover (coverage), prove runner, App::Yath for Test2. CPAN Testers smoke-tests every release across hundreds of platforms.
  • Docs: POD (Plain Old Documentation, embedded in .pm files); rendered by perldoc, pod2html, pod2man, Pod::Weaver (Dist::Zilla integration).
  • Distribution authoring: Dist::Zilla (declarative, plugin-based), Minilla, ExtUtils::MakeMaker (oldest, ubiquitous).
  • Notable users: Booking.com (huge Perl shop), DuckDuckGo (originally Perl), cPanel, Craigslist, Slack (early backend bits), BBC iPlayer, IMDB (early), Amazon (early order pipeline), DreamHost, the kernel build system on many distros, git’s Git.pm and many hooks.

Gotchas

  • Contextwantarray is real. my $count = @array; gives length, my ($x) = @array; gives first element. Surprising if you come from JS/Python.
  • @_ aliasing@_ elements alias the caller’s args; $_[0] = "x" mutates the caller’s variable. Signatures fix this by copying.
  • Boolean truthiness0, '0', '', undef are false; '0.0', '00' are TRUE. Footgun.
  • Regex globals$1..$9, $&, $', $\`` are dynamic.&`/`/$`historically slowed every regex; mostly fixed but use named captures ((?…)and%+`) instead.
  • Stringification of refs"$ref" gives HASH(0x...). Data::Dumper or JSON::PP::encode_json for actual content.
  • Hash key ordering — randomized since 5.18 to defend against algorithmic complexity attacks. Don’t rely on order; use Tie::IxHash or arrays-of-pairs.
  • Smartmatch (~~) and given/when — experimental, deprecated. Avoid.
  • Indirect object syntax (new Foo @args) — ambiguous, deprecated; use Foo->new(@args).
  • UTF-8use utf8; makes the source file UTF-8; you still need :encoding(UTF-8) on filehandles. Encode::decode for bytes-in. The “Unicode bug” (1 byte = 1 char unless flagged) bites everyone once.
  • local is dynamic, not lexical — affects all called code until scope exit.
  • Scalar 0 but true'0E0' and '0 but true' are zero numerically but true in boolean context (used by DBI for “0 rows but success”).
  • open without three-arg form is unsafe (open FH, $user_input can pipe-shell). Always open my $fh, '<', $path.

Citations