Perl's object system has exactly three moving parts: references (for data), packages (for namespaces and method tables), and bless (which links a reference to a package). That's all there is. A Perl "object" is simply a reference that has been told which package to look up its methods in.
Understanding this deeply matters because every OOP framework (Moose, Moo, Class::Accessor) is built on these three primitives. When things go wrong, you debug at this level.
package Animal; use strict; use warnings; sub new { my ($class, %args) = @_; # bless takes: a REFERENCE, and a class name (string) # It stamps the reference so Perl knows where to look for methods my $self = bless { name => $args{name} // 'Unknown', sound => $args{sound} // '...', }, $class; # $class NOT 'Animal' — allows subclassing return $self; } # Methods: just subs where $_[0] (or $self) is the invocant sub name { my $self = shift; return $self->{name} } sub sound { my $self = shift; return $self->{sound} } sub speak { my $self = shift; printf "%s says: %s\n", $self->name, $self->sound; } # Class method — called on the package, not an instance sub kingdom { return 'Animalia' } package main; my $a = Animal->new(name => 'Parrot', sound => 'Squawk'); $a->speak(); # Peek under the hood print ref($a); # "Animal" — the blessing class print $a->isa('Animal'); # 1 print $a->can('speak'); # returns the coderef for speak() # Re-bless: change an object's class at runtime bless $a, 'Dog'; # $a is now a Dog (if Dog package exists)
bless $ref, $class modifies the reference in-place, stamping it with the class name. It returns the same reference. The stamp is stored in the reference itself — you can inspect it with ref($obj).$class not 'Animal' in constructors. When a subclass calls Animal->new(), $class is the subclass name. Hard-coding 'Animal' breaks inheritance — all instances would be blessed as Animal even when created via a subclass.->: $a->speak() is syntactic sugar for Animal::speak($a). Perl looks up the method in ref($a)'s package (or its @ISA chain), then calls it with $a as the first argument.can() returns the coderef for a method if the object can call it, or undef. Use this instead of eval { $obj->method } to check capability. It's also how duck-typing works in Perl.Perl inheritance is implemented through the @ISA array in a package. When Perl can't find a method in the current package, it searches the packages listed in @ISA, recursively. use parent is the modern way to set @ISA.
SUPER:: calls the parent's version of the current method — essential for cooperative inheritance where a child extends rather than replaces parent behaviour. The tricky part is that SUPER:: is resolved at compile time relative to the package it's written in, not the object's class.
package Animal; sub new { my ($c,%a)=@_; bless{name=>$a{name}},$c } sub name { $_[0]->{name} } sub describe { my $self = shift; return "Animal: " . $self->name; } package Dog; use parent -norequire, 'Animal'; # sets @Dog::ISA = ('Animal') # -norequire: don't load Animal.pm sub new { my ($class, %args) = @_; # Call parent constructor, then add Dog-specific data my $self = $class->SUPER::new(%args); $self->{breed} = $args{breed} // 'Mixed'; return $self; } sub describe { my $self = shift; # SUPER:: calls Animal::describe — extend, don't replace my $base = $self->SUPER::describe(); return $base . " (Dog, breed: " . $self->{breed} . ")"; } package GoldenRetriever; use parent -norequire, 'Dog'; sub describe { my $self = shift; return $self->SUPER::describe() . " [Golden!]"; } package main; my $g = GoldenRetriever->new(name=>'Buddy', breed=>'Golden'); print $g->describe(); # Animal: Buddy (Dog, breed: Golden) [Golden!] # Inspect the ISA chain print join(' -> ', $g->isa($_) ? $_ : () for qw(GoldenRetriever Dog Animal));
use parent -norequire, 'Animal': without -norequire, Perl tries to require Animal from disk. Use -norequire when both classes are in the same file (common in single-file examples and tests).$class->SUPER::new(%args): calling SUPER::new on $class (not $self) is critical in constructors. If you wrote $self->SUPER::new, you'd be calling the parent constructor on an already-constructed object. Always use the class variable for super constructor calls.SUPER::describe() and appends to the result, creating a layered string. This is the proper pattern — each class is responsible for its own additions without duplicating parent logic.@ISA. For diamond inheritance, use use mro 'c3' which implements the same linearization algorithm as Python 3. SUPER:: always follows the MRO.AUTOLOAD is a special sub that Perl calls when a method is not found anywhere in the ISA chain. It receives the fully-qualified method name in $AUTOLOAD. This is how many accessor-generation modules work — they intercept unknown method calls and create accessors on the fly. DESTROY is called automatically when an object's reference count drops to zero — Perl's destructor.
UNIVERSAL is the invisible base class of every Perl object. It provides isa(), can(), and DOES() — available on every object without any inheritance declaration.
package AutoAccessor; use strict; use warnings; # Our AUTOLOAD uses 'our' because $AUTOLOAD is a package global our $AUTOLOAD; sub new { my ($class, %data) = @_; return bless \%data, $class; } sub AUTOLOAD { my $self = shift; # $AUTOLOAD is "Package::method_name" — strip the package prefix my $method = $AUTOLOAD; $method =~ s/.*:://; # remove "AutoAccessor::" # CRUCIAL: don't intercept DESTROY — it would suppress warnings return if $method eq 'DESTROY'; # Act as a getter/setter for any hash key if (exists $self->{$method}) { # Install the method permanently so AUTOLOAD isn't called again no strict 'refs'; *{"AutoAccessor::$method"} = sub { my $s = shift; $s->{$method} = shift if @_; # setter if arg given return $s->{$method}; }; return $self->{$method}; } die "No such attribute: $method\n"; } sub DESTROY { my $self = shift; print "Destroying: " . $self->{name} . "\n"; } package main; my $obj = AutoAccessor->new(name=>'Foo', value=>42); print $obj->name(); # triggers AUTOLOAD -> installs name() print $obj->name(); # calls installed name() directly — AUTOLOAD skipped $obj->value(99); # setter: sets $self->{value} = 99
DESTROY. When an object goes out of scope, Perl calls DESTROY. If your AUTOLOAD intercepts it (because you didn't return early), it suppresses the automatic destructor logic and can cause memory leaks and spurious warnings.*{"Package::method"} = sub { ... }. This stores the coderef in the package's symbol table. The next call goes directly to the installed method — AUTOLOAD is not invoked again. This is called method caching.no strict 'refs' is needed to use a variable as a glob name like *{"Package::$method"}. Strict mode normally forbids symbolic references (using a string as a variable name). This block-scoped pragma disables that check for just the next statement.$AUTOLOAD contains the full name including the package: "AutoAccessor::name". Always strip the package prefix with s/.*:://. Otherwise your attribute lookup would search for "AutoAccessor::name" in the hash instead of "name".The overload pragma lets you define what happens when standard Perl operators are applied to objects. This is how you make a Vector class support $v1 + $v2, or a BigNum class support $n > 100, or a custom class print meaningfully when interpolated in a string.
package Vector; use strict; use warnings; use overload '+' => \&add, '-' => \&subtract, '*' => \&scale, '""' => \&stringify, # called when object is used as string '==' => \&equal, 'abs' => \&magnitude, 'neg' => sub { Vector->new(-$_[0]->{x}, -$_[0]->{y}) }; sub new { my ($class, $x, $y) = @_; return bless { x => $x, y => $y }, $class; } # Operator handlers receive: ($left, $right, $swap) # $swap is true if the operands were reversed (e.g. 3 + $vec vs $vec + 3) sub add { my ($a, $b) = @_; return Vector->new($a->{x}+$b->{x}, $a->{y}+$b->{y}); } sub subtract { my ($a, $b, $swap) = @_; return $swap ? Vector->new($b->{x}-$a->{x}, $b->{y}-$a->{y}) : Vector->new($a->{x}-$b->{x}, $a->{y}-$b->{y}); } sub scale { Vector->new($_[0]->{x}*$_[1], $_[0]->{y}*$_[1]) } sub magnitude { sqrt($_[0]->{x}**2 + $_[0]->{y}**2) } sub stringify { "(" . $_[0]->{x} . "," . $_[0]->{y} . ")" } sub equal { $_[0]->{x}==$_[1]->{x} && $_[0]->{y}==$_[1]->{y} } package main; my ($v1, $v2) = (Vector->new(3,4), Vector->new(1,2)); print $v1 + $v2; # "(4,6)" — calls add(), then stringify() print $v1 * 3; # "(9,12)" — calls scale() print abs($v1); # "5" — calls magnitude(): sqrt(9+16)
($left, $right, $swap). $swap is 1 when Perl reversed the operands (e.g. 5 - $vec means $left=$vec, $right=5, $swap=1). Always handle $swap for non-commutative operators like -, /, and **."" (stringify) overload is the most important. It's called whenever Perl needs to convert your object to a string: in print, string interpolation, concatenation, warn, and error messages. Without it, objects print as Vector=HASH(0x...).ColorVector extends Vector without its own overloads, it uses Vector's. Return ref($a)->new(...) instead of Vector->new(...) in handlers to respect subclasses.0+ (numification — used in numeric context), bool (truthiness), @{}/%{}/${} (dereference as array/hash/scalar), <=>/cmp (for sort), x (repetition).Moose's type system goes far beyond the built-in types like Str and Int. You can create subtypes with custom validation constraints, coercions that automatically convert one type to another, and type unions. This is how you enforce business rules at the object-construction level rather than inside methods.
package MyTypes; use Moose::Util::TypeConstraints; # Subtype: a Str that must match a pattern subtype 'PositiveInt', as 'Int', where { $_ > 0 }, message { "$_ is not a positive integer" }; subtype 'EmailAddress', as 'Str', where { $_ =~ /^[\w.+-]+\@[\w-]+\.\w{2,}$/ }, message { "'$_' is not a valid email" }; # Coercion: automatically convert ArrayRef to comma-joined Str coerce 'Str', from 'ArrayRef', via { join(', ', @$_) }; package Person; use Moose; MyTypes->import(); has 'name' => ( is => 'ro', isa => 'Str', coerce => 1, # enable coercion for this attribute ); has 'age' => ( is => 'rw', isa => 'PositiveInt', # custom subtype — dies if <= 0 ); has 'email' => ( is => 'rw', isa => 'EmailAddress', predicate => 'has_email', # generates: $p->has_email() -> bool clearer => 'clear_email', # generates: $p->clear_email() ); package main; my $p = Person->new( name => ['John', 'Doe'], # ArrayRef coerced to "John, Doe" age => 30, email => 'john@example.com', ); # This would throw: "0 is not a positive integer" # Person->new(name => 'X', age => 0);
subtype ... as ... where ... message: the as parent determines what's checked first. Only values that pass the parent constraint reach the where block. The message block receives the failing value in $_ and provides a custom error string.coerce => 1. Without that, even if a coercion is defined globally, the attribute won't use it. This is intentional — it keeps behaviour explicit and predictable.predicate and clearer generate helper methods for optional attributes. predicate => 'has_email' creates $obj->has_email() which returns true if the attribute is defined. clearer resets it to undef.isa => 'Int | Str' accepts either. You can also use Maybe[Str] (Str or undef), ArrayRef[Int] (arrayref containing only integers), HashRef[Str], and nested parameterized types.Roles are Moose's answer to multiple inheritance. Where multiple inheritance creates fragile class hierarchies, roles compose cleanly — conflicts are detected at compile time. The around modifier is the most powerful: it receives the original method as a coderef and can completely control if and how it runs, enabling AOP-style patterns like logging, caching, and timing.
package Role::Printable; use Moose::Role; requires 'to_string'; # consuming class must implement this sub print_self { print $_[0]->to_string() . "\n" } package Role::Auditable; use Moose::Role; has '_log' => (is=>'rw', isa=>'ArrayRef', default=>sub{[]}); sub log_event { my ($self, $event) = @_; push @{$self->_log}, sprintf("[%s] %s", scalar localtime, $event); } sub audit_trail { return @{$_[0]->_log} } package BankAccount; use Moose; with 'Role::Printable', 'Role::Auditable'; # compose both roles has 'balance' => (is=>'rw', isa=>'Num', default=>0); sub deposit { my ($self, $amount) = @_; $self->balance($self->balance + $amount); } sub to_string { "Balance: \$" . $_[0]->balance } # 'around' wraps deposit: logs before AND after the real method around 'deposit' => sub { my ($orig, $self, $amount) = @_; # $orig is the original deposit() $self->log_event("Before deposit: $amount"); my $result = $self->$orig($amount); # call the REAL method $self->log_event("After deposit: balance=" . $self->balance); return $result; }; package main; my $acct = BankAccount->new(); $acct->deposit(100); $acct->print_self(); # from Role::Printable print $_, "\n" for $acct->audit_trail(); # from Role::Auditable
around receives ($orig, $self, @args). The first argument is the original method as a coderef. Call it as $self->$orig(@args). You can inspect arguments before passing them, modify the return value, or skip calling $orig entirely (caching).-alias or -excludes in the with statement, or by providing your own implementation._log attribute in Role::Auditable is composed into any consuming class. The attribute's storage is in the class instance — not shared between instances or in the role itself.before vs after vs around: before can abort (if it dies), but can't change the return value. after sees the result but can't change it. around has full control — use it for caching, input validation, or conditional execution.Every Moose class has a metaclass — an object that represents the class itself. Through $class->meta you can inspect and modify the class: list attributes, add methods, find superclasses, and even create new classes entirely at runtime. This is the Meta-Object Protocol (MOP).
package Point; use Moose; has $_ => (is=>'rw', isa=>'Num', default=>0) for qw(x y); sub to_string { "(".$_[0]->x.",".$_[0]->y.")" } package main; # $meta is the metaclass object for Point my $meta = Point->meta; # Introspect: list all attribute names print join(', ', sort $meta->get_attribute_list); # x, y # Introspect: get details about a specific attribute my $x_attr = $meta->get_attribute('x'); print $x_attr->type_constraint->name; # "Num" print $x_attr->is_required; # "" (false, has default) # Add a method to an existing class at runtime $meta->add_method('magnitude' => sub { my $self = shift; return sqrt($self->x**2 + $self->y**2); }); my $p = Point->new(x=>3, y=>4); print $p->magnitude; # 5 — method added at runtime works immediately # Create a new class entirely at runtime via metaclass my $dynamic_class = Moose::Meta::Class->create( 'DynamicPoint3D', superclasses => ['Point'], attributes => [ Moose::Meta::Attribute->new('z' => (is=>'rw',isa=>'Num',default=>0)) ], ); my $p3 = $dynamic_class->new_object(x=>1,y=>2,z=>3); print $p3->z; # 3
->meta returns the metaclass instance. In Moose, every class automatically gets a meta() method that returns a Moose::Meta::Class object. This object has methods for everything you can do declaratively (add attributes, methods, roles) plus inspection capabilities.add_method modifies a live class. New instances (and existing instances) gain the method immediately. This is legitimate for building framework code, test helpers, or plugins — but use it carefully in production as it bypasses Moose's declaration machinery.Moose::Meta::Class->create builds an entire class programmatically. This is how ORMs and other meta-frameworks work — they inspect a database schema and generate classes with the right attributes on the fly without any has declarations in source code.__PACKAGE__->meta->make_immutable at the end of a production Moose class. It compiles optimised accessor methods and freezes the class structure, providing a significant performance boost.Classic Perl objects store data in a hashref, and any code with the object can access $self->{name} directly — bypassing accessors and violating encapsulation. Inside-out objects store data in hashes keyed by the object's memory address inside the class, not in the object itself. The object is just an opaque scalar reference.
This is a technique you'll encounter in legacy code and libraries like Class::InsideOut. Understanding it deepens your knowledge of Perl's reference counting and memory model.
package SecureAccount; use strict; use warnings; use Scalar::Util qw(refaddr weaken); # Data stored OUTSIDE the object, keyed by memory address # %_balance is private to this package — callers cannot access it my %_balance; my %_owner; sub new { my ($class, %args) = @_; # The object is a blessed SCALAR ref (not a hash ref) my $self = bless \(my $dummy), $class; # refaddr returns the numeric memory address — unique per object my $id = refaddr($self); # Store data in package-level hashes indexed by memory address $_balance{$id} = $args{balance} // 0; $_owner{$id} = $args{owner}; return $self; } # Accessors use refaddr to find the right data slot sub balance { $_balance{refaddr($_[0])} } sub owner { $_owner{ refaddr($_[0])} } sub deposit { my ($self, $amt) = @_; $_balance{refaddr($self)} += $amt; } # DESTROY is MANDATORY — must clean up the external hash entries sub DESTROY { my $id = refaddr($_[0]); delete $_balance{$id}; delete $_owner{$id}; # memory leak if you forget this! } package main; my $acct = SecureAccount->new(owner=>'Alice', balance=>1000); $acct->deposit(500); printf "%s: \$%d\n", $acct->owner, $acct->balance; # Alice: $1500 # print $acct->{balance}; # ERROR: $acct is not a hashref!
refaddr($ref) from Scalar::Util returns the numeric memory address of a reference. This is unique for each live object. It's the key insight: use the address as a hash key to look up that object's data in package-level storage.bless \(my $dummy), $class creates a reference to an anonymous scalar. Callers get back an opaque blessed reference — they literally cannot access the data without going through the accessors.DESTROY is mandatory with inside-out objects. When the object is destroyed, its slot in %_balance and %_owner is not automatically freed — those are separate lexical hashes. Forgetting DESTROY creates a memory leak: the data grows without bound.delete from the hash in DESTROY, object B would inherit A's stale data. Always clean up in DESTROY.Perl uses reference counting for garbage collection. When two objects hold references to each other (a circular reference), their counts never reach zero — they leak forever. Scalar::Util::weaken() marks a reference as "weak": it doesn't count toward reference counting and becomes undef automatically when the referent is destroyed. This is how parent-child relationships are safely modelled.
use strict; use warnings; use Scalar::Util qw(weaken isweak refaddr); package Node; sub new { my ($class, $name) = @_; return bless { name => $name, parent => undef, children => [] }, $class; } sub add_child { my ($self, $child) = @_; push @{$self->{children}}, $child; # child holds a reference BACK to parent — this would be circular # WITHOUT weaken, neither would ever be garbage-collected $child->{parent} = $self; weaken($child->{parent}); # make the back-reference WEAK # Now: parent's refcount is NOT incremented by this assignment } sub DESTROY { print "Destroying: $_[0]->{name}\n"; } package main; { # inner scope — objects destroyed when scope ends my $root = Node->new('root'); my $child = Node->new('child'); $root->add_child($child); print isweak($child->{parent}) ? "parent ref is weak\n" : "parent ref is strong\n"; # When $root leaves scope, it's destroyed because child's # weak parent reference doesn't hold a strong ref to it } # prints "Destroying: child" then "Destroying: root" # If we had NOT used weaken(), neither would ever be destroyed # because: root keeps child alive, child keeps root alive — cycle
weaken($ref) modifies the reference in-place. After weakening, the reference still points to the object and works normally. But it no longer increments the reference count. When all strong references to the object are gone, it's destroyed, and the weak reference becomes undef.isweak($ref) to verify. Also, Scalar::Util::blessed($ref) returns the class name of a blessed ref (like ref() but returns undef for non-objects instead of the ref type). Use blessed for type checking in library code.tie lets you attach a class to a regular Perl variable — scalar, array, hash, or filehandle — so that ordinary operations like $x = 5, push @a, 1, or $h{key} trigger your class's methods. This is "transparent magic" — code using the tied variable doesn't know anything special is happening.
Classic uses: variables that persist to disk on assignment, hashes that enforce unique values, read-only scalars, logging variables, and environment-variable hash overlays.
package TiedScalar; # Must implement TIESCALAR, FETCH, STORE, DESTROY for a tied scalar sub TIESCALAR { # called when tie() is invoked my ($class, %opts) = @_; return bless { value => $opts{default}, min => $opts{min}, max => $opts{max}, history => [], }, $class; } sub FETCH { # called when reading $scalar return $_[0]->{value}; } sub STORE { # called when writing $scalar = value my ($self, $new_val) = @_; # Range validation on every assignment if (defined $self->{min} && $new_val < $self->{min}) { die "Value $new_val below minimum $self->{min}\n"; } if (defined $self->{max} && $new_val > $self->{max}) { die "Value $new_val above maximum $self->{max}\n"; } push @{$self->{history}}, $self->{value}; $self->{value} = $new_val; } sub DESTROY {} package main; use strict; use warnings; # tie $scalar, 'ClassName', constructor_args... tie my $temp, 'TiedScalar', default => 20, min => -273, max => 1000; print $temp; # 20 — triggers FETCH $temp = 37; # triggers STORE, logs 20 to history $temp = 100; # triggers STORE # $temp = 2000; # dies: "Value 2000 above maximum 1000" # Access the underlying tied object to inspect history my $obj = tied $temp; print join(', ', @{$obj->{history}}); # "20, 37" # untie removes the magic, returns to normal variable untie $temp;
TIESCALAR (constructor), FETCH (read), STORE (write), DESTROY. For tied arrays: TIEARRAY, FETCH, STORE, FETCHSIZE, STORESIZE, PUSH, POP, etc. For tied hashes: TIEHASH, FETCH, STORE, EXISTS, DELETE, FIRSTKEY, NEXTKEY.tied $var returns the underlying tied object. This is the only way to access the implementation object directly — calling its private methods or inspecting internal state. Without this, the tie is truly transparent.Tie::File (array tied to a file), Tie::IxHash (hash that preserves insertion order), DB_File/GDBM_File (hashes persisted to disk files), and Readonly (makes variables read-only by tying them to a class that dies on STORE).When pure Perl isn't fast enough, XS (eXternal Subroutines) is the standard way to write Perl extension modules in C. Inline::C is a friendlier alternative that compiles C code embedded directly in a Perl file — ideal for learning and one-off optimisation without a full module structure.
The key concepts are: the Perl C API (SV, AV, HV), the argument stack, and the XS typemap that converts between Perl scalars and C types.
use strict; use warnings; use Inline C => <<'END_C'; /* C code compiled and linked at runtime by Inline::C */ /* Fast integer GCD using Euclidean algorithm in C */ long fast_gcd(long a, long b) { while (b) { long t = b; b = a % b; a = t; } return a; } /* Sieve of Eratosthenes — returns count of primes up to n */ int count_primes(int n) { char *sieve = (char *)calloc(n + 1, sizeof(char)); int count = 0; for (int i = 2; i <= n; i++) { if (!sieve[i]) { count++; for (int j = i*2; j <= n; j += i) sieve[j] = 1; } } free(sieve); return count; } END_C # Perl calls C functions as if they were Perl subs printf "GCD(48, 36) = %d\n", fast_gcd(48, 36); # 12 printf "Primes up to 1M: %d\n", count_primes(1_000_000); # 78498 # XS typemap handles the Perl-C type conversion automatically: # Perl integer (SvIV) <-> C int/long # Perl float (SvNV) <-> C double # Perl string (SvPV) <-> C char*
use Inline C => '...' compiles the C code on first run using the system C compiler, caches the result in _Inline/, and links it into the running Perl process. Subsequent runs use the cache. The compilation only happens when the C code changes.SV* (scalar value) is converted to int/long/double/char* going in, and back to a Perl scalar on return..xs file (C with XS directives), a typemap file for type conversions, and a Makefile.PL. Use h2xs or Module::Build to scaffold it. Inline::C is perfect for prototyping before moving to full XS.Perl has three concurrency models: fork-based (separate processes, no shared memory), threads (shared memory, complex synchronisation), and event-loop/async (single-threaded, non-blocking I/O via IO::Async or AnyEvent). Fork is the most idiomatic and safest for CPU-bound work. Threads in Perl are expensive and have significant caveats.
use strict; use warnings; use Parallel::ForkManager; # CPAN — manages a pool of child processes use POSIX qw(WNOHANG); # Process 12 URLs with max 4 parallel workers my @urls = map { "https://api.example.com/item/$_" } 1..12; my %results; my $pm = Parallel::ForkManager->new(4); # Callback: called in PARENT process when a child finishes # $data_structure is what the child passed to finish() $pm->run_on_finish(sub { my ($pid, $exit, $ident, $signal, $core, $data) = @_; if (defined $data) { %results = (%results, %$data); # merge child's result } }); for my $url (@urls) { my $pid = $pm->start($url) and next; # --- CHILD PROCESS CODE --- # This block runs in a forked child. No shared memory with parent. my $data = fetch_url($url); # hypothetical HTTP fetch # Pass result back to parent via serialised data structure $pm->finish(0, { $url => $data }); # --- END CHILD CODE --- } $pm->wait_all_children; # blocks until all children complete # Raw fork() pattern (lower level): my $pid = fork(); die "fork failed: $!" unless defined $pid; if ($pid == 0) { # CHILD: $pid == 0 in child process print "Child PID: $$\n"; exit 0; } else { # PARENT: $pid is the child's PID waitpid($pid, 0); # wait for specific child }
$pm->start($ident) and next — the idiomatic Parallel::ForkManager loop. start() forks a child. In the parent, it returns the child's PID (a true value), so and next skips the rest of the loop body. In the child, it returns 0 (false), so the child proceeds into the work block.Parallel::ForkManager serializes the hashref with Storable and passes it via a pipe.fork(): $pid == 0 in the child, $pid > 0 (child's PID) in the parent, undef on failure. Always die on undef. Always call waitpid or wait in the parent — otherwise children become zombies, consuming process table entries.use threads) vs fork: threads share memory but require locks (threads::shared, Thread::Semaphore). They're heavy (Perl duplicates the interpreter per thread) and many CPAN modules aren't thread-safe. For most tasks, fork is simpler and more reliable.Every Perl package has a symbol table — a hash stored as %PackageName:: where each key is a symbol name and each value is a typeglob (*symbol). A typeglob is a container that simultaneously holds up to six "slots": scalar, array, hash, code, filehandle, and format. This is how Exporter works — it copies coderefs from one package's symbol table into another.
use strict; use warnings; # --- Inspect a package's symbol table --- package MyLib; sub hello { "hello" } sub world { "world" } our $VERSION = '1.0'; package main; # %MyLib:: is the symbol table hash for my $sym (sort keys %MyLib::) { my $code = *{"MyLib::$sym"}{CODE}; # extract CODE slot from glob print "$sym => " . (defined $code ? "sub" : "var") . "\n"; } # --- Install a method at runtime (used by import, Exporter, etc.) --- { no strict 'refs'; # required for symbolic glob assignment # Assign to the CODE slot of the glob — adds/replaces a sub *{"main::greet"} = sub { "Hello, $_[0]!" }; } print greet("World"); # "Hello, World!" — installed method works # --- How Exporter works (simplified) --- sub my_import { my ($from_pkg, $to_pkg, @funcs) = @_; no strict 'refs'; for my $fn (@funcs) { # Copy the CODE glob slot from source to destination package *{"${to_pkg}::${fn}"} = \&{"${from_pkg}::${fn}"}; } } my_import('MyLib', 'main', 'hello', 'world'); print hello() . " " . world(); # "hello world"
%MyLib:: is a real hash you can iterate over. Keys are symbol names; values are typeglobs. Note the double colon at the end of the package name.*{"Pkg::name"}{CODE}, {SCALAR}, {ARRAY}, {HASH}, {IO}, {FORMAT}. You can install only a specific slot without touching the others. Assigning a coderef to the CODE slot (*foo = \&bar) creates an alias, not a copy.\&{"PackageName::subname"} takes a reference to a named function using a string. This requires no strict 'refs'. It's how Exporter copies functions between packages and how many AUTOLOAD implementations install methods permanently.import() mechanism: when you write use Foo qw(bar), Perl calls Foo::import('Foo', 'bar'). Exporter-based modules implement import() as the glob-copying function above. This is the entire magic behind all use Module qw(...) imports.Perl programs don't simply run top-to-bottom. There are distinct compilation phases. Understanding them is essential for advanced metaprogramming, writing pragmas, and debugging mysterious "use constant" or "use strict" failures. BEGIN runs immediately when compiled (before anything else in the file executes). END runs after the program exits. CHECK, INIT, and UNITCHECK run at different transition points.
use strict; use warnings; # BEGIN runs as SOON as it is compiled — before the rest of the file BEGIN { print "1. BEGIN — compile time\n"; # use statements are compiled to: BEGIN { require M; M->import() } # That's why constants from 'use constant' are available immediately } # Multiple BEGINs run in order of appearance BEGIN { print "2. second BEGIN\n" } # UNITCHECK: after the current unit (file or eval) is compiled UNITCHECK { print "3. UNITCHECK\n" } # CHECK: after ALL compilation is done, before runtime CHECK { print "4. CHECK\n" } # INIT: first thing at runtime INIT { print "5. INIT — start of runtime\n" } print "6. Runtime code\n"; # END: runs when the program exits (even on die), LIFO order END { print "7. END\n" } # Practical BEGIN use: conditionally load modules based on Perl version BEGIN { if ($] >= 5.020) { require feature; feature->import('say', 'state'); } } # use constant is really: BEGIN { *NAME = \$value } in the symbol table use constant { MAX_RETRIES => 3, TIMEOUT => 30, }; # Perl inlines constant values at compile time — no hash lookup at runtime # END cleanup: always runs, even after die or exit() END { unlink '/tmp/my_lockfile'; # guaranteed cleanup }
BEGIN runs at compile time, immediately. This means it runs even under perl -c (syntax check only). That's why use Foo is safe to put anywhere — it compiles to BEGIN { require Foo; Foo->import() } and the module is loaded before any runtime code.BEGIN blocks run in order, but all run before any runtime code, no matter where in the file they appear. This surprises beginners: a BEGIN at line 100 runs before a bare print at line 5.use constant is compile-time magic. It installs a constant sub in the symbol table during BEGIN. Perl's optimizer inlines constant values — MAX_RETRIES is replaced with 3 at compile time, making it as fast as a literal number with zero runtime lookup cost.END blocks are a safety net for cleanup. They run after exit(), after uncaught die(), and even after signals (if you have a signal handler). Multiple END blocks run in reverse order (LIFO) — the last-declared runs first, which is the right order for teardown of nested resources.$] variable holds the Perl version as a number (e.g. 5.038001). $^O is the operating system name. $0 is the script name. $$ is the current PID. These special variables, documented in perlvar, are your runtime introspection toolkit.