Modules · Data · 1.00
Object::Proto
Objects stored as arrays, with slots fixed at compile time and prototype chains.
An alternative to a blessed hash. A class names its slots once, at compile time. Each object is an array, and each accessor a custom op that reads its slot by position. Objects are blessed into their class, so isa, can and your own methods work as they always have.
use Object::Proto;
BEGIN {
Object::Proto::role('Audited', 'changes:Int:default(0)');
object 'Account',
'owner:Str:required:readonly',
'balance:Num:default(0)';
Object::Proto::with('Account', 'Audited');
}
sub Account::deposit { my ($self, $n) = @_; $self->balance($self->balance + $n) }
# a modifier: every deposit is counted
Object::Proto::after('Account::deposit', sub { $_[0]->changes($_[0]->changes + 1) });
my $acct = new Account owner => 'ada', balance => 10;
$acct->deposit(5) for 1 .. 3;
print $acct->balance, ' after ', $acct->changes, " changes\n"; # 25 after 3 changes
print Object::Proto::does($acct, 'Audited') ? "audited\n" : '';
eval { $acct->balance('lots') };
print $@; # Type constraint failed for 'balance': expected Num
What you get
-
Slots fixed at compile time
A class names its slots once and each object is an array. An accessor is a custom op that reads its slot by position, and the function form,
age($obj), is faster again. -
Types and rules on the slot
'balance:Num:default(0)': a type checked inline, andrequired,readonly,lazywith a builder,trigger,weak,private, a predicate and a clearer. -
Prototype chains
A slot an object has not set is read from its prototype, and from that one's, as far up as the chain goes. Setting it on the object leaves the prototype untouched.
-
Roles and modifiers
Roles bring slots and methods and can require methods of the class, and
before,afterandaroundwrap a method. Neither costs anything in a class that does not use it. -
BUILD, DEMOLISH and singletons
BUILDruns once every argument is in place.DEMOLISHgets aDESTROYonly when it is defined.singletongives a class one lazily madeinstance. -
Lock and freeze
Object::Proto::lockstops new properties until it is undone, andfreezestops every change for good.clonemakes an unfrozen shallow copy.
In use
use Object::Proto;
BEGIN { object 'Cat', 'name:Str:required', 'sound:Str', 'lives:Int:default(9)' }
# a prototype: a slot the object has not set is read from it
my $cat = new Cat name => 'Cat', sound => 'meow';
my $tom = new Cat name => 'Tom';
$tom->set_prototype($cat);
print $tom->sound, "\n"; # meow
print Object::Proto::has_own_property($tom, 'sound') ? '' : "from its prototype\n";
$tom->sound('purr'); # its own now; the prototype is untouched
print $tom->sound, ' / ', $cat->sound, "\n"; # purr / meow
Object::Proto::freeze($tom);
eval { $tom->lives(8) };
print $@; # Cannot modify frozen object
Measured in its POD
One run, as the POD records it, with no machine named. Making an object, setting a slot and reading it: Object::Proto 3.5 million a second with methods and 5.4 million with the function form, a hand-written hash class 1.8 million, an array class 2.1 million. A bare hash with no class at all does 6.0 million, faster than any of them.
Setting and reading an object already made: the function form 32.7 million a second, methods 13.9 million, the hash class 7.0 million, a bare hash 26.0 million.
For C
object_types.h, found through ExtUtils::Depends: another XS module registers a type from its BOOT section with a C check function, which its POD puts at about 5 cycles a check against about 100 for a Perl callback.