# Classical OO Before the [`class`](../../p5/core/perlfunc/class) feature landed in 5.38, Perl's OO was built from three primitives: a package, a reference, and the [`bless`](../../p5/core/perlfunc/bless) function. Those three primitives are still present — the modern `class` keyword sits on top of the same machinery — and every pre-5.38 codebase, plus most of CPAN, still uses them directly. This chapter is for reading and maintaining that code. If you are writing a new class, start with the [modern class chapter](classes) instead. ## The three primitives 1. **A package** defines a namespace. Any package can be a class; the difference is purely how it is used. 2. **A reference** holds instance data. The convention is a hash reference, but any reference type works. 3. **[`bless`](../../p5/core/perlfunc/bless)** tags the referent with a class name, so that method dispatch through the reference looks up subs in that package. That is the whole model. Everything else — constructors, accessors, inheritance, roles — is a convention built on top. ## A minimal class ```perl package File; sub new { my ($class, %args) = @_; my $self = { path => $args{path}, content => $args{content}, }; return bless $self, $class; } sub path { $_[0]{path} } sub content { $_[0]{content} } sub print_info { my $self = shift; print "This file is at $self->{path}\n"; } 1; ``` Key conventions, each load-bearing: - **`sub new`** is the constructor. It is an ordinary method; the language has no constructor keyword. By convention it is named `new` and returns the blessed reference. - **`my ($class, %args) = @_`** unpacks the invocant first, then the named arguments. A class-method call `File->new(path => ...)` passes the class name as the first argument. - **`bless $self, $class`** attaches the class tag. Using `$class` (the actual invocant) rather than a hard-coded `'File'` is what makes the constructor *inheritance-safe*: a subclass inheriting `new` will bless into the subclass's name. - **`return` is usually omitted** after `bless` because `bless` returns its first argument. `bless $self, $class` as the last expression in the sub is idiomatic. We write it explicitly here for readability. ## Accessor methods by hand The model gives you no accessors; you write them. The terse form exploits the fact that `$_[0]` is the invocant: ```perl sub path { $_[0]{path} } sub content { $_[0]{content} } ``` The named-parameter form is clearer and what you should write when the accessor does anything non-trivial: ```perl sub path { my $self = shift; return $self->{path}; } ``` A read/write accessor: ```perl sub name { my $self = shift; $self->{name} = shift if @_; return $self->{name}; } ``` This is the boilerplate the [modern class feature](classes) replaces with `:reader` and `:writer`. ## Why hash references Hash references dominate because they let you add and rename fields without changing the layout, and because named keys survive a `Data::Dumper` or `JSON::encode` round-trip. Array references are faster and more compact but fragile: renumbering a slot breaks every access site. Hash-backed instances have one serious drawback: no encapsulation. Any caller can reach into the object's internals with `$obj->{path}`. The classical model has no tool to stop them — self-discipline and code review are the only defences. This is the single biggest reason to prefer the [modern class feature](classes) for new code. ## Inheritance with `@ISA` Inheritance is a package-level list named `@ISA`. Method dispatch walks it when the current package does not define the method. ```perl package File::MP3; our @ISA = ('File'); sub print_info { my $self = shift; $self->SUPER::print_info; print "Title: $self->{title}\n"; } 1; ``` `SUPER::` means "look for this method starting one step up the ISA chain from the package the current method was *compiled in*" — not from the object's class. That subtlety matters in diamond inheritance; the [inheritance chapter](inheritance) covers it. ### Declaring the parent — four ways There are four spellings of the same idea. Prefer the third for new code you are still maintaining in the classical style. ```perl # 1. Raw @ISA assignment. package Child; our @ISA = ('Parent'); # 2. use base. package Child; use base 'Parent'; # deprecated in modern Perl # 3. use parent. package Child; use parent 'Parent'; # current recommendation for classical # 4. @ISA with a require. package Child; require Parent; our @ISA = ('Parent'); ``` `use parent` handles the load of the parent module and the `@ISA` push in one line. `use base` is an older variant that also imports `Exporter`-style fields; avoid it. ## The `$self` invocant Every method is a subroutine whose first argument is either the class name (for class methods like `new`) or the object (for instance methods). The convention is to shift it into a variable named `$self`: ```perl sub method { my $self = shift; ... } ``` Or unpack it together with other arguments: ```perl sub method { my ($self, $arg1, $arg2) = @_; ... } ``` There is no runtime enforcement. If you forget to shift `$self`, your first user-supplied argument silently becomes `$self` and chaos ensues. Static analysers catch this; the language does not. ## Destructors A `DESTROY` method, if defined, runs when the object's refcount falls to zero. Use it to release external resources: ```perl sub DESTROY { my $self = shift; close $self->{fh} if defined $self->{fh}; } ``` Pitfalls that bite every project at least once: - `DESTROY` runs at unpredictable times. Do not rely on it for correctness-critical cleanup — explicit `$obj->close` beats implicit destruction. - `DESTROY` can see a partially constructed object if `new` croaked mid-way. Defensive `defined` checks are cheap insurance. - During global destruction (`END` time), dependencies may already be gone. A `DESTROY` that walks references can fail with "Attempt to free unreferenced scalar" or similar. Wrap risky cleanup in `eval { ... }`. ## `AUTOLOAD` If a method call finds no matching sub in the class or its ancestors, Perl looks for an `AUTOLOAD` sub. The called method's name appears in `our $AUTOLOAD`: ```perl our $AUTOLOAD; sub AUTOLOAD { my $self = shift; my $name = $AUTOLOAD; $name =~ s/.*:://; return if $name eq 'DESTROY'; # critical ... } ``` The `DESTROY` guard is mandatory: without it, object destruction triggers `AUTOLOAD` with `$name = 'DESTROY'` and you either misbehave or crash. `AUTOLOAD` is powerful and easy to abuse. The cases it earns its keep: - Lazy accessor generation — synthesise an accessor on first call, then install it as a real sub via typeglob assignment so the next call is direct. - Forwarding — delegate unknown methods to a contained object (but see [Roles and delegation](roles-and-delegation) for a cleaner approach). Cases where it hurts more than it helps: pretending to be a full-blown MOP, catching typos as silent no-ops, implementing "classes" whose entire surface is `AUTOLOAD`. ## A worked example A classical `Counter` class with read/write accessor and inheritance: ```perl package Counter; sub new { my ($class, %args) = @_; my $self = { value => $args{value} // 0, step => $args{step} // 1, }; return bless $self, $class; } sub value { my $self = shift; $self->{value} = shift if @_; return $self->{value}; } sub step { $_[0]{step} } sub tick { my $self = shift; $self->{value} += $self->{step}; } 1; ``` A subclass that doubles the step: ```perl package Counter::Double; use parent 'Counter'; sub new { my ($class, %args) = @_; $args{step} //= 2; return $class->SUPER::new(%args); } 1; ``` Compared with the [modern class](classes) version, the classical form is about three times as many lines and every one of them is a place to get it wrong. ## When classical OO is the right call Despite everything, there are legitimate reasons to stick with `bless`-based classes: - **You are maintaining existing code.** Rewriting a working classical class into `class` form is a migration project, not a free lunch — see the [migration chapter](migrating). - **You need to support older Perl.** If your code must run on Perl before 5.38, the `class` feature is unavailable. - **You need array-backed or code-backed objects.** `class` only creates hash-backed instances. If you are building a performance- critical structure with array-reference instances, classical `bless` is still the answer. - **You are doing something the `class` feature deliberately forbids** — reopening the package from outside, multiple inheritance, or deep metaprogramming via `@ISA` manipulation. ## A historical note on CPAN OO systems Because the classical model leaves so much to convention, CPAN grew an ecosystem of modules that layer declarative syntax on top: - **Moose** — feature-complete OO system with a type miniature, roles, method modifiers, a full introspection API, and a large extension ecosystem. Heavy at load time; the dominant choice for large applications built between 2007 and roughly 2020. - **Moo** — a subset of Moose's API without the introspection layer. Lighter, pure-Perl, API-compatible enough to let a Moo class and a Moose class share an inheritance tree. - **Class::Accessor** — generates simple accessors and `new`. Minimal, pure-Perl, no roles. - **Class::Tiny** — the smallest of the accessor-generator modules. All accessors are read/write, no type checks. - **Object::Pad** — the experimental prototype that became the core `class` feature. Still installable on CPAN for code that needs the feature on older Perls. - **Role::Tiny** — role composition for projects that do not use Moose but still want `does`-style sharing. These systems are **historical context** in pperl. New code in pperl should use the core [`class`](../../p5/core/perlfunc/class) feature, which covers the ground that Moose/Moo pioneered without the load-time and dependency cost. When you inherit a codebase that uses one of them, learn just enough of its surface to stay productive and plan the migration with the [migration chapter](migrating).