Attributes#
A sub attribute is a colon-prefixed annotation between the sub’s name (or signature) and its body. Attributes mark a sub as having some special property — being a method, being an lvalue, having a prototype, being constant, being subject to some third-party mechanism — that the runtime, the parser, or a user-written attribute handler can act on.
sub greet :method ($name) { ... }
sub editable :lvalue { ... }
sub mypush :prototype(\@@) { ... }
sub PI :const { 3.14159 }
sub handler :Memoize { ... } # third-party attribute
The four attribute steps#
To follow how an attribute affects a sub, distinguish four phases:
Parse. The compiler reads
:foo(args)and breaks it into a name (foo) and an optional parameter string (args).Register. The compiler invokes either a built-in handler (for
:lvalue,:method,:prototype(...)) orMODIFY_CODE_ATTRIBUTESfrom the relevant package (for user-defined attributes).Store. Built-in attributes set a flag on the CV (the compiled sub object). User attributes are stored wherever the handler chooses.
Query. Anyone can later ask
attributes::get(\&sub)for the attribute list. Built-in attributes are also queryable through more direct APIs (prototype,is_lvalue, …).
The four phases are why attributes are heterogeneous in behaviour: built-in attributes are runtime concepts; user attributes are whatever code in MODIFY_CODE_ATTRIBUTES decides to do with them.
Built-in attributes#
:lvalue#
Marks the sub as usable on the left-hand side of an assignment. The body’s last expression must be an lvalue. See lvalue and context.
sub editable :lvalue { $self->{x} }
$obj->editable = 42; # writes through to $self->{x}
:method#
Hint to the parser and to introspection that this sub is meant to be called as a method. The flag is mostly informational; it also suppresses prototype effects (since methods don’t honour prototypes anyway):
package Counter;
sub increment :method {
my $self = shift;
$self->{n}++;
}
:method does not automatically use parent or set @ISA; it does not change dispatch. It is a documentation / intent marker that the runtime also uses to skip prototype processing.
:prototype(...)#
The attribute form of a prototype, required when signatures are enabled in the same scope:
use feature 'signatures';
sub mygrep :prototype(&@) ($code, @list) {
grep { $code->($_) } @list;
}
The body’s parameter list is the signature; the attribute carries the prototype. Without the attribute form, the parser cannot tell which parenthesised list is which.
:const#
Promises the runtime that the sub’s body produces a constant result, enabling more aggressive inlining than the bare () prototype gives:
sub PI :const { 3.14159265358979 }
:const was introduced for use by use constant and similar tools. Most code should reach for use constant rather than applying :const directly.
Third-party attributes#
Any package can register attribute handlers for code refs by implementing MODIFY_CODE_ATTRIBUTES. The classic example is Attribute::Handlers, which provides a more declarative API on top of the raw mechanism. A user attribute looks identical to a built-in at the call site:
use Some::Memoize; # provides ':Memoize' attribute
sub expensive :Memoize {
...
}
The package providing the attribute decides what :Memoize means. Common third-party attributes:
:Memoize— automatically cache return values (fromMemoize).:Logged— wrap the sub with logging entry/exit (from various AOP-style modules).:Test,:Benchmark— testing-framework markers.
When you see a colon-attribute that isn’t on the built-in list above, find the package supplying it.
How attributes are written#
Several attributes can be chained. The ordering on disk is the ordering they’re processed in:
sub fancy :method :prototype(\@) :Memoize {
...
}
Attributes attach to both a forward declaration and the definition; if you write both, the attribute lists must agree:
sub greet :method; # forward declaration with :method
sub greet :method ($name) { # body, same attribute
...
}
A signature, when present, comes after attributes:
sub greet :method ($name) { # OK
...
}
sub greet ($name) :method { # error: attributes must precede signature
...
}
What attributes do not do#
They are not type annotations.
:Intis not a built-in; attempts to read it that way will silently do nothing unless some package has registered a handler that means the same thing.They are not metadata for the test framework unless the test framework registered a handler for them. Sprinkling
:testedon subs does nothing without code on the other side reading the annotation.They are not optimisation directives.
:fastdoes nothing.
Querying a sub’s attributes#
use attributes;
sub demo :method :prototype($) :Memoize { ... }
my @attrs = attributes::get(\&demo);
# returns: ('method', 'prototype($)', 'Memoize')
This is the introspection equivalent of prototype — it answers ”what annotations does this sub carry?“ rather than ”what does this sub look like to the parser?“.
See also#
Declaration — where the
:attrsyntax fits in the declaration grammar.Prototypes —
:prototype(...)is one attribute among several.Lvalue and context — the meaning of
:lvalue.prototype— runtime introspection of one specific built-in attribute.Object-Oriented Programming —
:methodand the broader OO context.