ZetCode

Perl class feature

last modified November 7, 2026

For most of its history, Perl had no syntax for classes. An object was a reference blessed into a package, a constructor was a subroutine that built a hash and called bless, and every accessor was written by hand or generated by a module such as Moose or Moo. Perl 5.38 introduced a new object system in the core, based on the Corinna design. It adds the keywords class, field, and method, generates the constructor, and stores the object's data in fields that are invisible outside the class.

The feature is still experimental, so a program enables it with use experimental 'class', which turns on the feature and silences the experimental warnings. It has grown with each release: Perl 5.40 added the :reader attribute and the __CLASS__ keyword, Perl 5.42 added :writer and lexical methods. The examples use use v5.40 and use v5.42 for the newer attributes; they were run with Perl 5.44 on Linux. The traditional bless-based approach is covered in the Perl OOP article.

A first class

A class is declared with the class keyword, its data with field, and its behaviour with method.

first.pl
#!/usr/bin/perl

use v5.40;
use experimental 'class';

class Person {
    field $name :param;
    field $occupation :param;

    method info {
        return "$name is a $occupation";
    }
}

my $p = Person->new(name => 'Jana', occupation => 'gardener');

say $p->info;
say ref $p;

The Person class has two fields. The :param attribute makes each of them a constructor parameter with the same name. We do not write a new subroutine; Perl generates it and it accepts named arguments. Inside a method, the fields are ordinary lexical variables and the object is available as $self, which is declared automatically. The ref function returns the class name, just as for a blessed reference.

$ perl first.pl
Jana is a gardener
Person

The same class with bless

For comparison, here is the equivalent class written in the classic style, using a signature-based constructor.

bless.pl
#!/usr/bin/perl

package Person;

use v5.40;

sub new($class, %args) {
    my $self = {
        name       => $args{name},
        occupation => $args{occupation},
    };

    return bless $self, $class;
}

sub info($self) {
    return "$self->{name} is a $self->{occupation}";
}

package main;

my $p = Person->new(name => 'Jana', occupation => 'gardener');

say $p->info;

$p->{ocupation} = 'teacher';
say $p->info;

The constructor has to collect the arguments, build the hash, and bless it. The object is a plain hash, so any code can read and write its keys. The last lines show a classic bug: the misspelt key ocupation silently creates a new entry, and the program prints the old value without any warning. With the class feature, a misspelt field name is a compile-time error, because fields are lexical variables checked by strict.

$ perl bless.pl
Jana is a gardener
Jana is a gardener

Fields

A field can be a scalar, an array, or a hash. It can have an initializer expression, which is evaluated for each new object and may refer to fields declared before it.

fields.pl
#!/usr/bin/perl

use v5.40;
use experimental 'class';

class Order {
    field $id :param;
    field @items;
    field %qty;
    field $label = "order-$id";

    method add($item, $count = 1) {
        push @items, $item unless exists $qty{$item};
        $qty{$item} += $count;
        return $self;
    }

    method summary {
        my $lines = join ', ', map { "$_ x$qty{$_}" } @items;
        return "$label: $lines";
    }
}

my $o = Order->new(id => 17);
$o->add('apple', 3)->add('bread')->add('apple');

say $o->summary;

say builtin::reftype($o);

eval { my $x = $o->{id} };
print "error: $@";

The @items array keeps the order in which items were added and the %qty hash their quantities; both start empty for every object. The $label field is computed from $id, which is declared earlier. The add method returns $self, so calls can be chained. The object's underlying type, reported by reftype, is the new OBJECT type; it is not a hash, so trying to reach inside it with $o->{id} fails. The only way to the data is through the methods.

$ perl fields.pl
order-17: apple x4, bread x1
OBJECT
error: Not a HASH reference at fields.pl line 31.

Constructor parameters

The :param attribute connects a field to a constructor argument. A parameter without a default is mandatory; one with a default is optional.

params.pl
#!/usr/bin/perl

use v5.40;
use experimental 'class';

class Server {
    field $host :param;
    field $port :param = 80;
    field $timeout :param //= 30;
    field $retries :param ||= 3;
    field $secure :param(tls) = 0;

    method show {
        say "$host:$port timeout=$timeout retries=$retries tls=$secure";
    }
}

Server->new(host => 'example.com')->show;
Server->new(host => 'example.com', port => 443, tls => 1)->show;
Server->new(host => 'example.com', timeout => undef, retries => 0)->show;

for my $args ([port => 8080], [host => 'a.com', prot => 8080]) {
    eval { Server->new(@$args) };
    print "error: $@";
}

A default given with = is used when the argument is missing. The //= default also applies when the argument is undef, and ||= when it is false, so timeout => undef and retries => 0 both fall back to the defaults. The form :param(tls) gives the parameter a different name than the field. The generated constructor checks its arguments: it dies when a mandatory parameter is missing and, unlike most hand-written constructors, also when it receives an unknown one, which catches typos such as prot.

$ perl params.pl
example.com:80 timeout=30 retries=3 tls=0
example.com:443 timeout=30 retries=3 tls=1
example.com:80 timeout=30 retries=3 tls=0
error: Required parameter 'host' is missing for "Server" constructor at params.pl line 23.
error: Unrecognized parameters for "Server" constructor: prot at params.pl line 23.

Readers and writers

The :reader attribute generates a method that returns the field's value. Perl 5.42 added :writer, which generates a method that sets it.

reader.pl
#!/usr/bin/perl

use v5.42;
use experimental 'class';

class Book {
    field $title  :param :reader;
    field $author :param :reader(written_by);
    field $price  :param :reader :writer;
    field @tags   :reader;

    method add_tag($tag) {
        push @tags, $tag;
        return $self;
    }
}

my $b = Book->new(title => 'Dune', author => 'Frank Herbert', price => 9.9);

say $b->title;
say $b->written_by;

$b->set_price(12.5)->add_tag('sci-fi')->add_tag('classic');

say $b->price;
say join ', ', $b->tags;
say scalar $b->tags;

A reader is named after the field unless a name is given, as in :reader(written_by). A writer is named set_ plus the field name and returns the object, which makes it chainable. A reader for an array field returns the elements in list context and their count in scalar context. Fields without these attributes stay completely private.

$ perl reader.pl
Dune
Frank Herbert
12.5
sci-fi, classic
2

ADJUST blocks

An ADJUST block runs during construction, after the fields have been initialized. It is the place for validation and for fields computed from several parameters.

adjust.pl
#!/usr/bin/perl

use v5.40;
use experimental 'class';

class Temperature {
    field $celsius :param;
    field $fahrenheit;

    ADJUST {
        die "below absolute zero: $celsius\n" if $celsius < -273.15;
        $fahrenheit = $celsius * 9 / 5 + 32;
    }

    method show {
        printf "%.1f °C = %.1f °F\n", $celsius, $fahrenheit;
    }
}

Temperature->new(celsius => $_)->show for 0, 36.6, -40;

eval { Temperature->new(celsius => -300) };
print "error: $@";

The block rejects impossible temperatures and computes the $fahrenheit field. Dying in ADJUST aborts the constructor, so an invalid object is never returned. A class may have several ADJUST blocks; they run in the order in which they appear, and those of a parent class run before those of a subclass.

$ perl adjust.pl
0.0 °C = 32.0 °F
36.6 °C = 97.9 °F
-40.0 °C = -40.0 °F
error: below absolute zero: -300

Inheritance

A class inherits from another with the :isa attribute. Only a single parent is allowed, and it must itself be declared with class.

inherit.pl
#!/usr/bin/perl

use v5.40;
use experimental 'class';

class Shape {
    field $name :param :reader;

    method area { 0 }

    method describe {
        return sprintf "%s with area %.2f", $name, $self->area;
    }
}

class Circle :isa(Shape) {
    field $r :param;

    method area { 3.14159265 * $r ** 2 }
}

class Rectangle :isa(Shape) {
    field $w :param;
    field $h :param;

    method area { $w * $h }

    method describe {
        my $base = $self->SUPER::describe;
        return "$base (${w}x$h)";
    }
}

my @shapes = (
    Circle->new(name => 'circle', r => 2),
    Rectangle->new(name => 'rectangle', w => 3, h => 4),
);

for my $s (@shapes) {
    say $s->describe;
    say '  isa Shape: ', $s isa Shape ? 'yes' : 'no';
}

Circle and Rectangle override area, and describe in Shape calls it on $self, so each shape uses its own version. Rectangle also overrides describe and calls the parent's version with SUPER::. The generated constructor accepts the parameters of the whole hierarchy, here name together with the subclass's own. Fields are private to the class that declares them: a Rectangle method cannot see $name and has to call the name reader. The isa operator tests class membership.

$ perl inherit.pl
circle with area 12.57
  isa Shape: yes
rectangle with area 12.00 (3x4)
  isa Shape: yes

Private methods

A method declared with my method is lexical: it is visible only inside the class body and cannot be called from outside. Inside the class, it is invoked with the ->& operator. Both were added in Perl 5.42.

private.pl
#!/usr/bin/perl

use v5.42;
use experimental 'class';

class Password {
    field $hash;

    ADJUST {
        $hash = 'unset';
    }

    my method digest($text) {
        my $sum = 0;
        $sum = ($sum * 31 + ord) % 1_000_003 for split //, $text;
        return sprintf '%06x', $sum;
    }

    method set($text) {
        die "too short\n" if length $text < 8;
        $hash = $self->&digest($text);
        return $self;
    }

    method check($text) {
        return $self->&digest($text) eq $hash;
    }
}

my $pw = Password->new->set('correct horse');

say $pw->check('correct horse') ? 'match' : 'no match';
say $pw->check('battery staple') ? 'match' : 'no match';

eval { $pw->digest('x') };
print "error: $@";

The digest method computes a toy checksum of a string. It is an implementation detail, so it is lexical: the public methods set and check call it with $self->&digest(...), while the call $pw->digest from outside fails because no such method exists in the package. The checksum only illustrates the technique; real code would use a proper password hashing module.

$ perl private.pl
match
no match
error: Can't locate object method "digest" via package "Password" at private.pl line 35.

Class data

There is no special syntax for data shared by all objects. A lexical variable declared with my inside the class block serves the purpose, and a plain sub can act as a class method.

counter.pl
#!/usr/bin/perl

use v5.40;
use experimental 'class';

class Counter {
    my $instances = 0;

    field $id = ++$instances;
    field $count :param = 0;

    sub instances { $instances }

    method inc($by = 1) { $count += $by; $self }

    method show { say "counter $id: $count" }
}

my $c1 = Counter->new;
my $c2 = Counter->new(count => 10);

$c1->inc->inc;
$c2->inc(5);

$_->show for $c1, $c2;
say 'instances: ', Counter::instances();
say 'instances: ', Counter->instances;

Each new object increments $instances in the initializer of its $id field, so the objects are numbered. The instances subroutine is not a method, so it has no $self and can be called on the class, either as a function or as Counter->instances; in the second form the class name arrives as an argument that the subroutine ignores. Calling a real method on the class name fails with the error Cannot invoke method "show" on a non-instance.

$ perl counter.pl
counter 1: 2
counter 2: 15
instances: 2
instances: 2

A stack class

The last example puts the pieces together in a small bounded stack.

stack.pl
#!/usr/bin/perl

use v5.40;
use experimental 'class';

class Stack {
    field $limit :param = 5;
    field @items;

    method push(@values) {
        die "stack overflow\n" if @items + @values > $limit;
        push @items, @values;
        return $self;
    }

    method pop {
        die "stack empty\n" unless @items;
        return pop @items;
    }

    method peek { $items[-1] }
    method size { scalar @items }
    method is_empty { !@items }
}

my $s = Stack->new(limit => 3);
$s->push(1, 2);

say 'size: ', $s->size;
say 'peek: ', $s->peek;
say 'pop: ', $s->pop;

$s->push(3, 4);

eval { $s->push(5, 6) };
print "error: $@";

say 'size: ', $s->size;
say 'pop: ', $s->pop until $s->is_empty;

eval { $s->pop };
print "error: $@";

The stack keeps its elements in a private array field and its capacity in a parameter with a default. Methods take signatures, so push accepts any number of values; it checks the capacity before adding anything, so a failing call leaves the stack unchanged. Method names may coincide with built-in functions: inside the class, push @items, ... still calls the built-in, while $s->push calls the method.

$ perl stack.pl
size: 2
peek: 2
pop: 2
error: stack overflow
size: 3
pop: 4
pop: 3
pop: 1
error: stack empty

Limitations

The core class system is deliberately small and still evolving. At the time of writing it has no roles, a class can inherit from only one parent, and that parent must be a class, not a package with bless. There is no built-in type checking for fields. Moose and Moo remain the choice for code that needs roles, type constraints, or support for older Perl versions; the class feature needs no dependencies and gives real encapsulation.

Best practices

In this article we have used the core Perl class feature: fields with initializers, constructor parameters with :param, readers and writers, ADJUST blocks, inheritance with :isa, lexical methods, and class data.

Source

perlclass · perlclassguts

Author

My name is Jan Bodnar, and I am a passionate programmer with extensive programming experience. I have been writing programming articles since 2007. To date, I have authored over 1,400 articles and 8 e-books. I possess more than ten years of experience in teaching programming.

List all Perl tutorials.