Object::Pad(3pm)

🌸 Object::Pad(3pm) 🌸

Okay bestie, welcome to the Object::Pad(3pm) manual page β€” literally the group chat where Perl gets a simple syntax for lexical field-based objects. πŸ’…

✨ NAME ✨

Object::Pad β€” a simple syntax for lexical field-based objects. That's the whole vibe: private variables that look like lexicals, doing object member field duty. πŸŽ€

✨ SYNOPSIS ✨

On perl version 5.26 onwards:

use v5.26;
use Object::Pad;

class Point {
   field $x :param = 0;
   field $y :param = 0;

   method move ($dX, $dY) {
      $x += $dX;
      $y += $dY;
   }

   method describe () {
      print "A point at ($x, $y)\n";
   }
}

Point->new(x => 5, y => 10)->describe;

Or, for older perls that lack signatures:

use Object::Pad;

class Point {
   field $x :param = 0;
   field $y :param = 0;

   method move {
      my ($dX, $dY) = @_;
      $x += $dX;
      $y += $dY;
   }

   method describe {
      print "A point at ($x, $y)\n";
   }
}

Point->new(x => 5, y => 10)->describe;

✨ DESCRIPTION ✨

This module gives you a simple syntax for creating object classes, and it uses private variables that look like lexicals as object member fields. Obsessed, honestly. πŸ’–

πŸ’« Relation to use feature 'class' πŸ’«

Since its original creation, this module has evolved into the test-bed for refining the design of what a good class-based object system would look like for Perl. While it's useful in its own right, it also acts as a preview of what Perl's use feature 'class' syntax will likely become. Being a CPAN module means it can be iterated much faster than core Perl, which only gets one major release per year. Getting the design right in this module first therefore helps reduce the amount of development time needed in core Perl. 🌈

Where features overlap, both this module and the class feature added to recent Perl versions should largely agree on syntax and semantics. This module will likely always be ahead of core Perl in terms of exact abilities because it is acting as a test-bed for these new ideas. However, where both implementations support the same idea, they should in general use the same syntax and semantics to provide it. πŸ¦‹

If you're interested in using this object system in new code in a way that's forward-compatible with the feature added directly in newer Perl versions, while also supporting versions of Perl before this feature was added, you may wish to use instead the module Feature::Compat::Class. That module enables the core Perl feature on the latest version of Perl, or uses Object::Pad to fill in the missing syntax and features on older Perl versions. Slay. ✨

πŸ§ͺ Experimental Features πŸ§ͺ

Since version 0.63.

While most of this module has evolved into a stable state, recently-added parts remain experimental because the design is still evolving, and many features and ideas have yet to be implemented. Of these experimental features, feel free to try them out in newly-developed code, but don't complain if a later version is incompatible with your current code and you'll have to change it. Once individual features are stable and no longer declared experimental, they should have the same stability guarantees as any other long-term supported CPAN module, or core Perl itself. 🫢

That all said, please do get in contact if you find the module overall useful. The more feedback you provide in terms of what features you are using, what you find works, and what doesn't, will help the ongoing development and hopefully eventual stability of the design β€” both within this CPAN module and as the wider experiment to provide it as a core Perl feature. See the FEEDBACK section. πŸ’Œ

Features of this module that are currently marked as experimental provoke warnings in the experimental category, unless silenced. You can silence this with no warnings 'experimental' but then that will silence every experimental warning, which may hide others unintentionally. For a more fine-grained approach you can instead use the import line for this module to only silence the module's warnings selectively:

use Object::Pad ':experimental(mop)';

use Object::Pad ':experimental(custom_field_attr)';

use Object::Pad ':experimental(composed_adjust)';

use Object::Pad ':experimental(inherit_field)';

use Object::Pad ':experimental(apply_phaser)';

use Object::Pad ':experimental(lexical_class)';

use Object::Pad ':experimental(:all)';  # all of the above

Since version 0.64.

Multiple experimental features can be enabled at once by giving multiple names in the parens, separated by spaces:

use Object::Pad ':experimental(mop custom_field_attr)';

Since version 0.810 attempting to request all of the experiments at once by using an empty :experimental() is currently accepted, but yields a warning. This may be removed in future. πŸ‘€

πŸ› οΈ Automatic Construction πŸ› οΈ

Classes are automatically provided with a constructor method, called new, which helps create the object instances. This may respond to passed arguments, automatically assigning values of fields, and invoking other blocks of code provided by the class. It proceeds in the following stages:

🎬 The BUILDARGS phase

If the class provides a BUILDARGS class method, that is used to mangle the list of arguments before the BUILD blocks are called. Note this must be a class method not an instance method (and so implemented using sub). It should perform any SUPER chaining as may be required.

@args = $class->BUILDARGS( @_ )

🧺 Field assignment

If any field in the class has the :param attribute, then the constructor will expect to receive its arguments in an even-sized list of name/value pairs. This applies even to fields inherited from the parent class or applied roles. It is therefore a good idea to shape the parameters to the constructor in this way in roles, and in classes if you intend your class to be extended. πŸ’…

The constructor will also check for required parameters (these are all the parameters for fields that do not have default initialisation expressions). If any of these are missing an exception is thrown. 🚨

πŸ—οΈ The BUILD phase

As part of the construction process, every BUILD phaser of every component class will be invoked. Each will be passed in the list of arguments the constructor was invoked with as arguments. Each class should perform its required setup behaviour, but does not need to chain to the SUPER class first; this is handled automatically. 🫑

🌟 The ADJUST phase

Next, every ADJUST phaser of every component class is invoked. This happens after the fields are assigned their initial values and the BUILD blocks have been run. ✨

πŸ” The strict-checking phase

Finally, before the object is returned, if the :strict(params) class attribute is present, then the constructor will throw an exception if there are any remaining named arguments left over after assigning them to fields as per :param declarations, and running any ADJUST blocks. No sneaky leftovers, bestie. πŸ’€

✨ KEYWORDS ✨

🌟 class 🌟

class Name :ATTRS... {
   ...
}

class Name :ATTRS...;

Behaves similarly to the package keyword, but provides a package that defines a new class. Such a class provides an automatic constructor method called new. πŸ‘‘

As with package, an optional block may be provided. If so, the contents of that block define the new class and the preceding package continues afterwards. If not, it sets the class as the package context of following keywords and definitions. πŸŽ€

As with package, an optional version declaration may be given. If so, this sets the value of the package's $VERSION variable.

class Name VERSION { ... }

class Name VERSION;

An optional list of attributes may be supplied in similar syntax as for subs or lexical variables. (These are annotations about the class itself; the concept should not be confused with per-object-instance data, which here is called fields). 🧬

Whitespace is permitted within the value and is automatically trimmed, but as standard Perl parsing rules, no space is permitted between the attribute's name and the open parenthesis of its value:

:attr( value here )     # is permitted
:attr (value here)      # not permitted

The following class attributes are supported:

🧬 :isa

:isa(CLASS)

:isa(CLASS CLASSVER)

Since version 0.57.

Declares a superclass that this class extends. At most one superclass is supported. 🌳

If the package providing the superclass does not exist, an attempt is made to load it by code equivalent to

require CLASS;

and thus it must either already exist, or be locatable via the usual @INC mechanisms. πŸ“¦

The superclass may or may not itself be implemented by Object::Pad, but if it is not then see SUBCLASSING CLASSIC PERL CLASSES for further detail on the semantics of how this operates.

An optional version check can also be supplied; it performs the equivalent of

BaseClass->VERSION( $ver )

🎭 :does

:does(ROLE)

:does(ROLE ROLEVER)

Since version 0.57.

Composes a role into the class; optionally requiring a version check on the role package. πŸ’

Multiple roles can be composed by using multiple :does attributes, one per role.

The package will be loaded in a similar way to how the :isa attribute is handled. πŸ¦‹

πŸ«₯ :abstract

:abstract

Since version 0.817.

Declares that this class is abstract. An abstract class is permitted to define required methods (i.e. named methods without a body definition). 🧚

Instances may not be created of this class type directly. Instead, a subclass of this class must be derived that provides method bodies for any of the named required methods. Any subclass of an abstract class must either provide bodies for all remaining required methods, or themselves be declared :abstract as well. No half-finished main character energy allowed. πŸ’…

This provides a way in which a generic type of class can be created, that is intended for multiple different specialisations to further define different kinds of behaviour by providing bodies for those named methods. Being abstract means that instances cannot be created of this as-yet-incomplete class type. 🌈

πŸͺ„ :lexical_new

:lexical_new

Since version 0.822.

Causes the constructor for this class to be made available to the class body lexically, and not installed as a symbol in the package's symbol table. The effect of this is that code inside the class can make use of it as a lexical function, but it is not available to callers outside the class. In this way, a wrapper method in the class itself is fully in control of how, and when, instances of the class are constructed. πŸ”

For example, a singleton class could be created by providing a wrapper class method that lazily creates a single instance stored in a lexical variable in the class, ensuring every call returns the same instance. ✨

class A::Singleton :lexical_new {
   my $instance;

   method instance :common { $instance //= new(shift); }

   ...
}

Also!! Important detail, bestie β€” since methods don't count when Perl resolves bareword function call names, you can totally provide a :common class method called new that wraps the default constructor. Iconic for when you want your constructor to take arguments in some style other than the usual name/value pairs. πŸ’…

Like, say a class only has one parameter β€” sometimes the vibe is just to pass that one value as the only constructor argument. So:

class Path :lexical_new {
   field $filename :param :reader;

   method new :common ($filename) {
      return &new( $class, filename => $filename );
   }
}

my $null = Path->new( "/dev/null" );

But note carefully (fr fr, this one matters): to do this you must use method. You cannot use sub here 😭 β€” that would redefine the content of the lexical sub new, instead of creating a new package-named one.

:repr(TYPE)

Sets the representation type for instances of this class. Has to be one of these values:

:repr(native)

The native representation. It's an opaque representation type whose contents are not specified ✨. It only works for classes whose entire inheritance hierarchy is built only from classes based on Object::Pad.

:repr(HASH)

The representation will be a blessed hash reference. The instance data gets stored in an array referenced by a key called Object::Pad/slots, which is fairly unlikely to clash with existing storage on the instance. No other keys will be used β€” they're available for implementions and subclasses to use. The exact format of the value stored here is not specified and may change between module versions, though it can be relied on to be well-behaved as some kind of perl data structure for purposes of modules like Data::Dumper or serialisation into things like YAML or JSON. 🫢

:repr(keys)

Since version 0.803.

The representation will be a blessed hash reference. The instance data gets stored in individual keys of the hash, named after the class and the field variable name, separated by a / symbol. Objects in this representation should behave predictably with data printing modules like Data::Dumper or serialisation via YAML or JSON. πŸ’–

These two hash-based representation types may be useful when converting existing classes into using Object::Pad where there may be existing subclasses of it that presume a blessed hash for their own use.

:repr(magic)

The representation will use MAGIC to apply the instance data in a way that's invisible at the Perl level, and shouldn't get in the way of other things the instance is doing even in XS modules. 🧚

This representation type is the only one that will work for subclassing existing classes that do not use blessed hashes.

:repr(pvobj)

Since version 0.804.

The representation will be the SVt_PVOBJ type newly added to Perl, which offers more efficient storage for object instances. This is only available on Perl version 5.38.0 onwards.

Also, heads up: this is also newly-added and may not be fully tested and reliable yet. Once it has more real-world testing and has proven reliable it may become the default instance representation on versions of Perl where it is available. πŸ‘€

:repr(autoselect), :repr(default)

Since version 0.23.

This representation will select one of the representations above depending on what's best for the situation. Classes not derived from a non-Object::Pad base class will pick native, and classes derived from non-Object::Pad bases will pick either the HASH or magic forms depending on whether the instance is a blessed hash reference or some other kind.

This achieves the best combination of DWIM while still allowing the common forms of hash reference to be inspected by Data::Dumper, etc. This is the default representation type, and does not have to be specifically requested. Slay. πŸ‘‘

:strict(params)

Since version 0.43.

Can only be applied to classes that contain no BUILD phasers. If set, then the constructor will complain about any unrecognised named arguments passed to it (i.e. names that do not correspond to the :param of any defined field and left unconsumed by any ADJUST phaser). πŸ™ˆ

Since BUILD phasers can inspect the arguments arbitrarily, the presence of any such phaser means the constructor cannot determine which named arguments are not recognised.

This attribute is a temporary stepping-stone for compatibility with existing code. It's recommended to enable this whenever possible, as a later version of this module will likely perform this behaviour unconditionally whenever no BUILD phasers are present.


✨ class (anon) ✨

my $class = class :ATTRS... { ... };

Since version 0.809.

If a class keyword is not followed by a package name, it creates an anonymous class expression. This is an expression that yields a value suitable to use as a constructor invocant for creating instances of that class, without specifying what its package name will actually be. πŸ¦‹

This is useful for creating small one-off instances inline in expressions, such as in unit tests. Since it still accepts the usual attributes and inner body statements, it can be useful for creating one-off instances of roles, with required methods being applied.

my $testobj = (class {
   apply Role::Under::Test;
   method required { return "a useful value"; }
})->new;
Okay so, caveat time 😭 β€” due to limitations on how classes work in Perl, anonymous classes are still backed by long-lived named classes in the global symbol table, unlike true anonymous functions which can go out of scope and be reclaimed once no references to them remain in existence. This means that anonymous classes will retain references to any variables captured within them, even if the class expression itself goes out of scope and any instances created by it no longer remain.

🌸 role 🌸

role Name :ATTRS... {
   ...
}

role Name :ATTRS...;

Since version 0.32.

Similar to class, but provides a package that defines a new role. A role acts similar to a class in some respects, and differently in others. πŸ’œ

Like a class, a role can have a version, and named methods.

role Name VERSION {
   method a { ... }
   method b { ... }
}

A role does not provide a constructor, and instances cannot directly be constructed. A role cannot extend a class.

A role can declare that it requires methods of given names from any class that implements the role.

role Name {
   requires METHOD;
}

A role can provide instance fields. These are visible to any ADJUST phasers or methods provided by that role.

Since version 0.33.

role Name {
   field $f;

   ADJUST { $f = "a value"; }

   method f { return $f; }
}

Since version 0.57 a role can declare that it provides another role:

role Name :does(OTHERROLE) { ... }
role Name :does(OTHERROLE OTHERVER) { ... }

This will include all of the methods from the included role. Effectively this means that applying the outer role to a class will imply applying the other role as well. πŸŽ€

The following role attributes are supported:

:compat(invokable)

Since version 0.35.

Enables a form of backward-compatibility behaviour useful for gradually upgrading existing code from classical Perl inheritance or mixins into using roles.

Normally, methods of a role cannot be directly invoked and the role must be applied to an Object::Pad-based class in order to be used. This however presents a problem when gradually upgrading existing code that already uses techniques like roles, multiple inheritance or mixins when that code may be split across multiple distributions, or for some other reason cannot be upgraded all at once. Methods within a role that has the :compat(invokable) attribute applied to it may be directly invoked on any object instance. This allows the creation of a role that can still provide code for existing classes written in classical Perl that has not yet been rewritten to use Object::Pad. ⭐

The tradeoff is that a :compat(invokable) role may not create field data using the field keyword. Whatever behaviours the role wishes to perform must be provided only by calling other methods on $self, or perhaps by making assumptions about the representation type of instances. πŸ’€

It should be stressed again: this option is only intended for gradual upgrade of existing classical Perl code into using Object::Pad. When all existing code is using Object::Pad then this attribute can be removed from the role.

🌈 my class 🌈

my class Name :ATTRS... { ... }

Since version 0.819; experimental

If a class keyword is preceded by my it creates a class that has lexical visiblity, rather than being available globally via a fully-qualified name in the symbol table. This is useful for creating internal helper classes within modules, such as for returning complex structures, or simply to use internally without being visible to the caller. As the class itself is only visible lexically, callers in other scopes are unable to see it to create new instances of it. 🫢

class My::Module;

my class HelperStructure
{
   field $name :param :reader;
   field $value :param :reader;
}

method get_thing ()
{
   return HelperStructure->new(
      name  => "the name",
      value => "the value",
   );
}
Note: the above is technically a lie. 😭 Current versions of perl do not support truely anonymous packages to be used as classes, so even these "lexical" classes are in fact named on the symbol table. However, they are given a name that is syntactically-impossible for regular code to create or use, and a const-returning lexical function is created with the correct lexical name, which returns the true name of the class, so that the ->new method can still be called on it.

As a result of this limitation, there are a number of operations that lexical classes do not currently support β€” such as subclassing. It's hoped that later versions of either this module, or perl itself, will be able to expand on these abilities. For now, this feature remains an experimental best-effort basis.

Due to the way this lexical function works, it can stand in for the class name when used with the ->isa method. Note carefully here, that the lexical class is used directly as a bareword and not quoted. πŸ™ˆ

if( $obj->isa( HelperStructure ) ) { ... }

Also, bestie, quick little glow-up: because the right-hand side operand of the isa operator (in Perl version 5.32 or later) doesn’t need quoting, you can just drop it in raw like this:

if( $obj isa HelperStructure ) { ... }

🧬 inherit

inherit Classname;
inherit Classname VER;

inherit Classname LIST...;
inherit Classname VER LIST...;

Since version 0.807 ✨

This declares a superclass that your class extends. At most one superclass is supported, okay? If it’s there, this declaration has to come before any methods or fields are declared, or any roles are applied. Other compile-time declarations β€” like use statements that import utility functions or other behaviours β€” may be permitted before this, though, as long as they do not interact with the class structure in any way.

This is a newer form of the :isa attribute, and it’s intended to be more flexible if import arguments or other features get added later. Future-proofing, we love to see it πŸ’…

If the package providing the superclass does not exist, an attempt is made to load it by code equivalent to:

require Classname;

…and thus it must either already exist, or be locatable via the usual @INC mechanisms.

An optional version check can also be supplied; it performs the equivalent of:

Classname->VERSION( $ver )

Experimentally since version 0.807, an optional list of arguments can also be provided, in similar syntax to those in a use statement. Currently this list of arguments must be names of fields to be inherited. Only fields in the base class that are annotated with the :inheritable attribute may be inherited. Once a field is inherited, methods and other expressions in the class body can use that field identically to any fields defined by that class itself.

class Class1 {
   field $x :inheritable = 123;
}

class Class2 {
   inherit Class1 '$x';
   field $y = 456;
   method describe { say "Class2(x=$x,y=$y)" }
}

Class2->new->describe;

πŸŽ€ apply

apply Rolename;

apply Rolename VER;

Since version 0.807 ✨

Composes a role into the class, optionally requiring a version check on the role package. This is a newer form of the :does attribute, intended to be more flexible if import arguments or other features are added at a later time.

Multiple roles can be composed by using multiple :does attributes, one per role.

apply statements can be freely mixed with other statements inside the body of the class. In particular, an apply statement that adds fields or methods may appear before or after the class has defined some of its own. It is not required that they appear first. Mix and match, bestie 🫢


🌸 field

field $var;
field @var;
field %var;

field $var :ATTR ATTR...;

field $var = EXPR;

field $var //= EXPR;
field $var ||= EXPR;

field $var { BLOCK }

Since version 0.66 ✨

Declares that the instances of the class or role have a member field of the given name. This member field will be accessible as a lexical variable within any method declarations and ADJUST phasers in the class.

Array and hash members are permitted and behave as expected; you do not need to store references to anonymous arrays or hashes. No cap, it just works πŸ“

Member fields are private to a class or role. They are not visible to users of the class, nor inherited by subclasses nor any class that a role is applied to. In order to provide access to them, a class may wish to use method to create an accessor, or use the attributes such as :reader to get one generated.

The following field attributes are supported:

πŸ‘‘ :reader, :reader(NAME)

Since version 0.27 ✨

Generates a reader method to return the current value of the field. If no name is given, the name of the field is used. A single prefix character _ will be removed if present.

field $x :reader;

# equivalent to
field $x;  method x { return $x }

Since version 0.55 these are permitted on any field type, but prior versions only allowed them on scalar fields. The reader method behaves identically to how a lexical variable would behave in the same context; namely returning a list of values from an array or key/value pairs from a hash when in list context, or the number of items or keys when in scalar context.

field @items :reader;

foreach my $item ( $obj->items ) { ... }   # iterates the list of items

my $count = $obj->items;                   # yields count of items

✨ :writer, :writer(NAME)

Since version 0.27 ✨

Generates a writer method to set a new value of the field from its arguments. If no name is given, the name of the field is used prefixed by set_. A single prefix character _ will be removed if present.

field $x :writer;

# equivalent to
field $x;  method set_x { $x = shift; return $self }

Since version 0.28 a generated writer method will return the object invocant itself, allowing a chaining style:

$obj->set_x("x")
   ->set_y("y")
   ->set_z("z");

Since version 0.55 these are permitted on any field type, but prior versions only allowed them on scalar fields. On arrays or hashes, the writer method takes a list of values to be assigned into the field, completely replacing any values previously there.

πŸ’œ :mutator, :mutator(NAME)

Since version 0.27 ✨

Generates an lvalue mutator method to return or set the value of the field. These are only permitted for scalar fields. If no name is given, the name of the field is used. A single prefix character _ will be removed if present.

field $x :mutator;

# equivalent to
field $x;  method x :lvalue { $x }

Since version 0.28 all of these generated accessor methods will include argument checking similar to that used by subroutine signatures, to ensure the correct number of arguments are passed β€” usually zero, but exactly one in the case of a :writer method.

πŸ¦‹ :accessor, :accessor(NAME)

Since version 0.53 ✨

Generates a combined reader-writer accessor method to set or return the value of the field. These are only permitted for scalar fields. If no name is given, the name of the field is used. A prefix character _ will be removed if present.

This method takes either zero or one additional arguments. If an argument is passed, the value of the field is set from this argument (even if it is undef). If no argument is passed (i.e. scalar @_ is false) then the field is not modified. In either case, the value of the field is then returned.

field $x :accessor;

# equivalent to
field $x;

method x {
   $x = shift if @_;
   return $x;
}

🧚 :weak

Since version 0.44 ✨

Generated code which sets the value of this field will weaken it if it contains a reference. This applies to within the constructor if :param is given, and to a :writer accessor method. Note that this only applies to automatically generated code, not normal code written in regular method bodies. If you assign into the field variable you must remember to call Scalar::Util::weaken (or builtin::weaken on Perl 5.36 or above) yourself.

🌈 :param, :param(NAME)

Since version 0.41 ✨

Sets this field to be initialised automatically in the generated constructor. This is only permitted on scalar fields. If no name is given, the name of the field is used. A single prefix character _ will be removed if present.

Any field that has :param but does not have a default initialisation expression or block becomes a required argument to the constructor. Attempting to invoke the constructor without a named argument for this will throw an exception. In order to make a parameter optional, make sure to give it a default expression β€” even if that expression is undef:

field $x :param;          # this is required
field $z :param = undef;  # this is optional

Any field that has a :param and an initialisation block will only run the code in the block if required by the constructor. If a named parameter is passed to the constructor for this field, then its code block will not be executed.

Values for fields are assigned by the constructor before any BUILD phasers are invoked. Order matters, bestie πŸ“‹

πŸŽ€ :inheritable

Experimentally since version 0.807 fields may be optionally inherited when deriving a subclass from another. Not every field is allowed to be inherited. This attribute marks a field as being available for subclasses to inherit.

🌟 Field Initialiser Expressions

Since version 0.54 a deferred statement block is also permitted, on any field variable type. This permits code to be executed as part of the instance constructor, rather than running just once when the class is set up. Code in a field initialisation block is roughly equivalent to being placed in a BUILD or ADJUST phaser.

Since version 0.73 this may also be written as a plain expression introduced by an equals symbol (=). This is equivalent to using a block. Note carefully: the equals symbol is part of the field syntax; it is not simply a runtime assignment operator that happens once at the time the class is declared. Just like the block form described above, the expression is evaluated during the constructor of every instance.

Since version 0.74 this expression may also be written using a defined-or or logical-or assignment operator (//= or ||=). In these cases, the default expression will be evaluated and assigned if the caller did not pass a value to the constructor at all, or if the value passed was undef (for //=) or false (for ||=). For most scalar parameters, where undef is not a valid value, you probably wanted to use //= to assign defaults.

class Action {
   field $timeout :param //= 20;
   ...
}

# The default of 20 will apply here too
my $act = Action->new( timeout => $opts{timeout} );
Note that $self is specifically not visible during an initialiser expression. This is because the object is not yet fully constructed, so it would be dangerous to allow access to it while in this state. However, the __CLASS__ keyword is available, so initialiser expressions can make use of class-based dispatch to invoke class-level methods to help provide values.

Field initialier expressions were originally experimental, but since version 0.800 no longer emit experimental warnings. We love a stable glow-up πŸ’–

Since version 0.806, fields that you already declared in the class are visible during the initialisation expression of fields declared later β€” and yes, you can legitimately use the value they were assigned 🫢. If that earlier field had a :param declaration, it'll already have been filled in from the value the constructor got handed. But heads up, bestie: all ADJUST phasers run after the field initialisation expressions, so any values tweaked inside those phasers are simply not visible at this point. Timing is everything πŸ’….

Control flow that tries to leave a field initialiser expression or block? Not permitted, sorry 😭. That covers any return expression, any next/last/redo that's outside of a loop, any of those with a dynamically-calculated label expression, or with a label that it doesn't actually appear in. goto statements are also currently forbidden β€” though known-safe ones may get the green light in a future version ✨.

Loop control expressions that are known at compiletime to affect a loop they actually appear within? Those are totally fine πŸ‘‘

field $x { foreach(@list) { next; } }       # this is fine

field $x { LOOP: while(1) { last LOOP; } }  # this is fine too

has 🚫

Since version 0.813 this keyword is no longer recognised. It's gone, it's retired, it left the group chat.

It used to be an earlier version of what is now the field keyword:

has $var;
has @var;
has %var;

has $var = EXPR;

has $var { BLOCK }

Because of the one-shot immediate nature of these initialisation expressions (and a whole bunch of other reasons), the keyword was removed β€” rip to a legend πŸ’€.

If you need to evaluate an expression exactly once during the class declaration and assign its now-constant value to every instace, just store it in a regular my variable instead:

my $default_var = EXPR;
field $var = $default_var;

method ✨

method NAME {
   ...
}

method NAME (SIGNATURE) {
   ...
}

method NAME :ATTRS... {
   ...
}

method NAME;

Declares a new named method. It behaves similarly to the sub keyword, except that inside the method body all of the member fields are also accessible πŸ”₯. Bonus: the method body gets a lexical called $self holding the invocant object directly β€” it's already been shifted off the @_ array for you, no manual labour required πŸ’….

If the method has no body and is given simply as a name, this declares a required method for a role or abstract class. Any class that wants to be non-abstract has to provide it. It'll be a compiletime error to apply the role to a class, or derive from an abstract class, that doesn't provide this. No skipping homework πŸŽ€.

The signatures feature is automatically enabled for method declarations. And in this case the signature doesn't have to account for the invocant instance β€” that's handled directly for you ✨.

method m ($one, $two) {
   say "$self invokes method on one=$one two=$two";
}

...
$obj->m(1, 2);

A list of attributes may be supplied as for sub. The most useful of these is :lvalue, letting you build read-write accessors for fields with zero drama (but also peep the :reader, :writer and :mutator field attributes πŸ‘€).

class Counter {
   field $count;

   method count :lvalue { $count }
}

my $c = Counter->new;
$c->count++;

Every method automatically gets the :method attribute applied, which suppresses warnings about ambiguous calls resolved to core functions if the name of a method matches a core function. Iconic behaviour honestly πŸ‘‘.

The following additional attributes are recognised by Object::Pad directly:

:override

Since version 0.29.

Marks that this method expects to override another of the same name from a superclass. If the superclass doesn't provide such a method, it's an error at compiletime β€” the vibe check fails immediately πŸ’€.

:common

Since version 0.62.

Marks that this method is a class-common method, instead of a regular instance method. A class-common method may be invoked on class names instead of instances. Inside the method body you get a lexical $class rather than $self. And because it's not associated with a particular object instance, a class-common method simply cannot see instance fields β€” no cap 🫢.

method (lexical) 🧚

method $var { ... }

method $var :ATTRS... (SIGNATURE) { ... }

Since version 0.59.

Declares a new lexical method. Lexical methods are not visible via the package namespace β€” instead they're stored directly in a lexical variable (with the same scoping rules as regular my variables). You invoke them from subsequent method code in the same block using $self->$var(...) method call syntax 🌸.

class WithPrivate {
   field $var;

   # Lexical methods can still see instance fields as normal
   method $inc_var { $var++; say "Var was incremented"; }
   method $dec_var { $var--; say "Var was decremented"; }

   method bump {
      $self->$inc_var;
      say "In the middle";
      $self->$dec_var;
   }
}

my $obj = WithPrivate->new;

$obj->bump;

# Neither $inc_var nor $dec_var are visible here

This effectively gives you private methods, since they're inaccessible from outside the block that defines the class. And as a bonus there's zero chance of a name collision, because lexical variables in different scopes are independent even when they share the same name ✨. Super useful in roles or abstract base classes β€” you can create internal helper methods without letting callers see them, or risking their names colliding with other named methods defined on the consuming class. Main character energy, honestly πŸ’œ.

my method πŸ¦‹

my method NAME { ... }

Since version 0.814, lexical method declarations are supported using the my keyword prefix. These become available as lexical functions, rather than being stored in the class package. As a result they're not available by named method resolution, package ->can lookup, or via the MOP. They're a convenient alternative to the syntax above, where the method is stored anonymously via a lexical variable 🌈.

Since lexical methods aren't visible to named method resolution, you have to invoke them with function-call syntax β€” and remember to pass in the invocant as the first argument, bestie πŸ™ˆ:

my method inner { ... }

method outer {
   inner($self, @args);
}

BUILD πŸ”¨

BUILD {
   ...
}

BUILD (SIGNATURE) {
   ...
}

Since version 0.27.

Declares a builder phaser for this component class. A builder phaser may use subroutine signature syntax, just like methods, to help unpack its arguments. A build phaser is not a subroutine, so it's not permitted to use subroutine attributes (for example :lvalue) β€” rules are rules πŸ’….

Note that a BUILD phaser is a named phaser block and not a method. Attempts to create a method named BUILD (i.e. with syntax method BUILD {...}) will fail with a compiletime error, to avoid this confusion. Yes, it will yell at you 😭.

ADJUST πŸŽ€

ADJUST {
   ...
}

Since version 0.43.

Declares an adjust phaser for this component class. This block of code runs inside the constructor, after any BUILD phasers and after automatic field value assignment. It can make any final adjustments to the instance (like initialising fields from calculated values) ✨.

An adjust phaser is not a subroutine, so it's not permitted to use subroutine attributes (except see below πŸ‘€). And just so we're clear: an ADJUST phaser is a named phaser block and not a method β€” it doesn't use the sub or method keyword. But like with method, the member fields are accessible within the code body, and so is the special $self lexical 🫢.

Currently, an ADJUST phaser receives a reference to the hash containing the current constructor arguments, as per ADJUSTPARAMS (see below). This was added in version 0.66 but will be removed again, because it conflicts with the more flexible and generally nicer named-parameter ADJUST :params syntax (see below). Such uses should be considered deprecated πŸ’€. A warning will be printed to indicate this whenever an ADJUST phaser uses a signature β€” you can quiet that warning by using ADJUSTPARAMS instead. Additionally, a warning may be printed on code that attempts to access the params hashref via the @_ array.

Since version 0.801, in a future version of this module, ADJUST phasers may be implemented as true blocks and will not permit out-of-block control flow. At present they're implemented as one full CV per block, but a warning is emitted if out-of-block control flow is attempted 😬:

ADJUST {
   return;
}

Using return to leave an ADJUST phaser is discouraged and will be removed
in a later version at FILE line LINE.

Since version 0.805, an experimental feature can be enabled that puts all the ADJUST phasers into a single CV, rather than creating one CV for every phaser. This is currently being tested for stability, and may become the default behaviour in a future version. For now though you have to request it specially 🌸:

use Object::Pad ':experimental(composed_adjust)';

ADJUST :params πŸ‘‘

ADJUST :params ( :$var1, :$var2, ... ) {
   ...
}

ADJUST :params ( :$var1, :$var2, ..., %varN ) {
   ...
}

Since version 0.70; non-experimental since version 0.805.

An ADJUST phaser can be marked with a :params attribute, meaning it consumes additional constructor parameters by assigning them into lexical variables πŸ’–.

Before the phaser itself, a list of lexical variables is introduced inside parentheses. The name of each one is preceded by a colon, and it consumes a constructor parameter of the same name. These parameters are considered "consumed" for the purposes of a :strict(params) check.

A named parameter may be provided with a default expression, which gets evaluated if no matching named argument is provided to the constructor. As with fields, if a named parameter has no defaulting expression it becomes a required argument to the constructor β€” an exception is thrown by the constructor if it's absent 😭.

For example:

ADJUST :params ( :$x, :$y = "default", :$z ) { ... }

Note here that x and z are required parameters for the constructor of a class containing this phaser, but y is an optional parameter whose value will be filled in by the expression if it's not provided. And because these parameters are named and not positional, there's no ordering constraint β€” required and optional parameters can be freely mixed, slay πŸŽ‰.

Optional parameters can also use the //= and ||= operators to provide a default expression. In these cases, the default is applied if the caller didn't provide the named argument at all, or if the provided value was not defined (for //=) or not true (for ||=) πŸ¦‹.

ADJUST :params ( :$name //= "unnamed" ) { ... }

...okay so continuation time πŸ’… Like, same energy as regular subroutine signature params: every named parameter you declare is visible to the defaulting expression of all the ones that come after it. That means values can be calculated from other values, which is lowkey kind of iconic. For example:

ADJUST :params ( :$thing = undef, :$things = [ $thing ] ) {
   # Here, @$things is a list of values
}

So now the caller can pass a whole list of values through an array reference in the things parameter, or just one solitary value in thing. Slay, flexible queen ✨

The final element of the list can also be a regular hash variable β€” that's your signal to say "hey, cram all the remaining named parameters in here." The code in the phaser should delete from that hash any parameters it wants to consume, exact same drill as the earlier case above. 🫢

Heads up bestie: it is unspecified whether named fields or parameters belonging to subclasses that haven't been processed yet are visible to hashes of earlier superclasses. In the current implementation they are visible, but do not let your code rely on that fact. It's giving "works today, chaos tomorrow" πŸ’€

Also note β€” and this is a real one β€” there must be a space between the :params attribute and the parentheses holding the named parameters. If that space isn't there, perl parses the parentheses as if they're the value of the :params() attribute, and it will fail to parse the way you intended. Just like other attributes and subroutine signatures, this whitespace is significant. Respect the space πŸŽ€

(Also fun fact: this whole notation is borrowed from a plan to add named parameter support to perl's subroutine signature syntax. Lore drop πŸ‘‘)

πŸ’œ ADJUSTPARAMS

Since version 0.51.

ADJUSTPARAMS ( $params ) {    # on perl 5.26 onwards
   ...
}

ADJUST {
   my $params = shift;
   ...
}

A variant of an ADJUST phaser that gets handed a reference to the hash holding the current constructor parameters. Inside, $params is that hashref. Now here's the tea β˜• β€” this hash does not contain any constructor parameters already eaten by :param declarations on any fields; it's only the leftovers once those have been dealt with.

The code in the phaser should delete from this hash any parameters it wants to consume. And once all the ADJUST phasers have run, whatever keys are still chilling in the hash get treated as errors β€” subject to the :strict(params) check, obviously. So clean up after yourself, bestie 🧚

⭐ APPLY

Experimental. Since version 0.815.

APPLY ( $class_mop ) {        # on perl 5.26 onwards
   ...
}

APPLY {
   my $class_mop = shift;
   ...
}

This one is only valid inside a role definition. It declares a block of code that runs at compile-time every time the role is applied to a class. Every single time it gets applied to a new class, the code gets invoked again. It receives as an argument an Object::Pad::MOP::Class instance representing the class the role is currently being applied to. Fancy 🎩

The eventual intent is that having any of these phaser blocks around will replace the current implicit behaviour of applying a role β€” though currently they run in addition to it. It's part of an ongoing experiment, and the details will change over time. So like, don't get too attached to the specifics 😭

πŸ¦‹ __CLASS__

my $classname = __CLASS__;

Since version 0.72.

Only valid inside the body (or signature) of a method, an ADJUST phaser, or the initialising expression of a field. It yields the class name of the instance that the method, block or expression is being invoked on. ✨

It's similar to core perl's __PACKAGE__ constant, except β€” and this is the main character difference β€” it cares about the dynamic class of the actual instance, not the static class the code lives in. So when a subclass instance that inherited code from its superclass invokes it, you get the name of the class of the instance, no matter which class defined the code. Obsessed honestly πŸ’–

For example:

class BaseClass {
   ADJUST { say "Constructing an instance of " . __CLASS__; }
}

class DerivedClass :isa(BaseClass) { }

my $obj = DerivedClass->new;

Will produce the following output:

Constructing an instance of DerivedClass

This is especially useful in field initialisers, where you want to invoke (constant) methods on the invoking class to provide default values for fields. That way a subclass can totally provide a different value:

class Timer {
   use constant DEFAULT_DURATION => 60;
   field $duration = __CLASS__->DEFAULT_DURATION;
}

class ThreeMinuteTimer :isa(Timer) {
   use constant DEFAULT_DURATION => 3 * 60;
}

🌸 requires

requires NAME;

Declares that this role requires a method with the given name from any class that implements it. If the implementing class doesn't provide such a method, it's a compiletime error. No sneaking past this one 🚨

This form of declaring a required method is now vaguely discouraged, in favour of the bodyless method form described above. It's giving "still works, but we've moved on" πŸ’…


🌈 CREPT FEATURES 🌈

So these aren't strictly part of being an object system, but this module has nevertheless picked up a bunch of behaviours through feature creep, purely because people kept finding them useful. We've all been there 🫠

✨ Implied Pragmata ✨

To encourage users to write clean, modern code, the body of the class block might act as if the following pragmata are in effect:

use strict;
use warnings;
no indirect ':fatal';  # or  no feature 'indirect' on perl 5.32 onwards
use feature 'signatures';

Now, storytime 😭 This behaviour was designed early, around the original "line-0" version of the Perl 7 plan β€” which was subsequently found to be a bad design and abandoned. That leaves this module in a genuinely unfortunate situation, because its behaviour here does not match the plans for core perl; where the recently-added class keyword does none of this, although the method keyword always behaves as if signatures were enabled anyway.

So here's the deal:

If you import this module with a module version number of 0.821 or higher, this behaviour will not happen. That'll be the default behaviour in future versions, and it matches how the core perl feature works. (Though method will still always parse as if the signatures feature is enabled β€” that part stays.) In particular, this no longer applies a catch-all use warnings, meaning any prior declarations of use experimental or no warnings 'experimental::...' will not be overridden. πŸ™Œ

However β€” and this is the kindness part β€” in order to not upset older code that may have relied on these pragmata being present, module imports without such a version number will still enable them. This ensures code that hasn't yet been updated for the version 0.821 semantics isn't affected. Bestie code care πŸ’–

In addition, if you import this module with a module version number of 0.800 or higher (but less than 0.821), it will emit a warning if you forget to enable strict and warnings before using the class or role keywords:

use Object::Pad 0.800;

class X { ... }
class keyword enabled 'use strict' but this will be removed in a later version at FILE line 3.
class keyword enabled 'use warnings' but this will be removed in a later version at FILE line 3.

For newly-written code in a modern style, remember that a handy way to enable the strict and warnings pragmata for the entire file all at once is to use the use VERSION syntax at the top of the file. Requesting a version of v5.36 or later will already perform all of the pragmata listed above. Convenient queen πŸ‘‘

use v5.36;

πŸ“ Yield True

The following behaviour is likely to be removed in a later version of this module.

A class statement or block will yield a true boolean value. That means you can stick it directly inside a .pm file and skip the whole "explicitly yield a true value from the end of it" ritual. πŸŽ‰

But β€” as with the implied pragmata above β€” this behaviour has also been found to be a bad design and will likely be removed soon. For now it's suggested you don't rely on it, and instead either use the new module_true feature already part of the use v5.38 pragma, or on older perls simply remember to put an explicit true value at the end of the file. Future-proofing, bestie 🌟


πŸ‘‘ SUBCLASSING CLASSIC PERL CLASSES πŸ‘‘

There are a number of details specific to the case of deriving an Object::Pad class from an existing classic Perl class that isn't implemented using Object::Pad. It's a whole crossover episode πŸ’«

πŸŽ€ Storage of Instance Data

Instances will pick either the :repr(HASH) or :repr(magic) storage type. That's the vibe check, no third option 🌸

🧚 Object State During Methods Invoked By Superclass Constructor

It's common in classic Perl OO style to invoke methods on $self during the constructor. This is supported here since Object::Pad version 0.19. Note however that any methods invoked by the superclass constructor may not see the object in a fully consistent state. (This fact isn't specific to using Object::Pad β€” it would happen in classic Perl OO as well, so don't come for us πŸ™ˆ.) The field initialisers will have been invoked, but the BUILD and ADJUST phasers will not. Yeah. That's the timeline πŸ’€

For example, in the following:

package ClassicPerlBaseClass {
   sub new {
      my $self = bless {}, shift;
      say "Value seen by superconstructor is ", $self->get_value;
      return $self;
   }
   sub get_value { return "A" }
}

class DerivedClass :isa(ClassicPerlBaseClass) {
   field $_value = "B";
   ADJUST {
      $_value = "C";
   }
   method get_value { return $_value }
}

my $obj = DerivedClass->new;
say "Value seen by user is ", $obj->get_value;

Until the ClassicPerlBaseClass::new superconstructor has returned, the ADJUST phaser will not have been invoked. The $_value field will still exist, but its value will be "B" during the superconstructor. After the superconstructor, the BUILD and ADJUST phasers are invoked, before the completed object is returned to the user. The result will therefore be:

Value seen by superconstructor is B
Value seen by user is C

See, it all works out in the end 🫢✨

✨ STYLE SUGGESTIONS ✨

ok so none of this is required, like at all, but these are the bestie-approved best practices πŸ’… the whole point is that everyone's code looks consistent and nobody's out here reinventing vibes. Take notes, iykyk.

πŸŽ€ $VERSION declaration

so here's the tea 😭 it would be lovely if CPAN and all the other toolchain modules could parse the embedded version declarations inside class statements... but as of writing this (June 2020), literally none of them do. None. Zero. So yeah, you still gotta do the once-per-file $VERSION declaration, in the old syntax those modules can actually parse.

Also heads up: those modules won't parse the class declaration either, so you're gonna have to duplicate it with a package declaration alongside your class keyword. Yeah it means repeating the package name. Slightly tragic, we know. πŸ™ˆ

The hope is that eventually the upstream toolchain modules will get with the program and accept class syntax as sufficient to declare a package and set its version. Fingers crossed bestie 🫢

See also

🌸 File Layout

Start the file with a use Object::Pad line β€” ideally with a minimum-required version because we love a boundary. Then the toplevel package and class declarations for the file. And since it's at toplevel, there's no need for block notation β€” it can just be a unit class, easy ✨

And no, you do not need use strict or any of the other usual pragmata β€” the class keyword implies all of that. iconic behavior tbh πŸ‘‘

use Object::Pad 0.16;

package My::Classname 1.23;
class My::Classname;

# other use statements

# field, methods, etc.. can go here

πŸ¦‹ Field Names

Field names should follow pretty much the same rules as regular lexical variables in code β€” lowercase, name components separated by underscores. For tiny little examples like "dumb record" structures, that's honestly plenty.

class Tag {
   field $name  :mutator;
   field $value :mutator;
}

But once you're in a bigger example with lots of non-trivial method bodies, it gets confusing remembering where the field variables came from β€” because we no longer have that visual clue of $self->{ ... } telling us "hey, this is a field!" 😭 In those cases, the suggestion is to prefix the field names with a leading underscore, so they're way more visually distinct.

class Spudger {
   field $_grapefruit;

   ...

   method mangle {
      $_grapefruit->peel; # The leading underscore reminds us this is a field
   }
}

see? lowkey genius. that underscore is doing so much work πŸ’œ


🌈 WITH OTHER MODULES

πŸ’« Syntax::Keyword::Dynamically

There's a cross-module integration test that asserts dynamically works correctly on object instance fields. slay.

use Object::Pad;
use Syntax::Keyword::Dynamically;

class Container {
   field $value = 1;

   method example {
      dynamically $value = 2;
      ,..
      # value is restored to 1 on return from this method
   }
}

πŸ”₯ Future::AsyncAwait

As of Future::AsyncAwait version 0.38 and Object::Pad version 0.15, both modules now use XS::Parse::Sublike to parse blocks of code. Because of this, the two modules can operate together β€” which means class methods can be written as async subs that await expressions πŸ‘‘

use Future::AsyncAwait;
use Object::Pad;

class Example
{
   async method perform ($block)
   {
      say "$self is performing code";
      await $block->();
      say "code finished";
   }
}

And these three modules combine β€” there's additionally a cross-module test to make sure object instance fields can be dynamically set during a suspended async method. fr fr the teamwork is unmatched 🫢

🧚 Devel::MAT

When you're using Devel::MAT to help analyse or debug memory issues in programs that use Object::Pad, you're gonna want to additionally install the module Devel::MAT::Tool::Object::Pad. It gives you new commands and extends the existing ones so it can better assist with analysing details related to Object::Pad classes and instances of them. so helpful 😭

pmat> fields 0x55d7c173d4b8
The field AV ARRAY(3)=NativeClass at 0x55d7c173d4b8
Ix Field   Value
0  $sfield SCALAR(UV) at 0x55d7c173d938 = 123
...

pmat> identify 0x55d7c17606d8
REF() at 0x55d7c17606d8 is:
└─the %hfield field of ARRAY(3)=NativeClass at 0x55d7c173d4b8, which is:
...

πŸ’€ DESIGN TODOs

These following points are details about the design of pad field-based object systems in general β€” big-picture thinking hours:

  • Is multiple inheritance actually required, if role composition is implemented including giving roles the ability to use private fields?
  • Consider the visibility of superclass fields to subclasses. Do subclasses even need to be able to see their superclass's fields, or are accessor methods always appropriate?

Concrete example: The $self->{split_at} access that Tickit::Widget::HSplit makes of its parent class Tickit::Widget::LinearSplit.

πŸ“ IMPLEMENTATION TODOs

These points are more about this particular module's implementation:

  • Consider multiple inheritance of subclassing, if that is still considered useful after adding roles.
  • Work out why no indirect doesn't appear to work properly before perl 5.20.
  • Work out why we don't get a Subroutine new redefined at ... warning if we
sub new { ... }
  • The local modifier does not work on field variables, because they appear to be regular lexicals to the parser at that point. A workaround is to use Syntax::Keyword::Dynamically instead:
use Syntax::Keyword::Dynamically;

field $loglevel;

method quietly {
   dynamically $loglevel = LOG_ERROR;
   ...
}

🌟 FEEDBACK

These following resources are useful forms of providing feedback β€” especially if you wanna tell us what you find good or bad about the module, request new features, ask questions about best practice, etc... 🫢 we're literally listening:

πŸ‘‘ SPONSORS

With thanks to the following sponsors, who have helped me be able to spend time working on this module and other perl features. absolute legends, no cap πŸ’–

Additional details may be found at <https://github.com/Ovid/Cor/wiki/Sponsors>.

πŸŽ€ AUTHOR

Paul Evans <leonerd@leonerd.org.uk>


and that's the whole page bestie!! πŸ¦‹βœ¨ you made it to the end, so proud of you 🫢 this was perl v5.42.3, dated 2026-08-22, and it's Object::Pad(3pm) β€” go forth and write iconic classes πŸ’…πŸ‘‘

πŸ“¦ where this page comes from