Ecosyste.ms: Awesome

An open API service indexing awesome lists of open source software.

Awesome Lists | Featured Topics | Projects

https://github.com/sysread/slot

Fast, simple, compile-time class declaration for Perl
https://github.com/sysread/slot

class object oo perl slot

Last synced: about 2 months ago
JSON representation

Fast, simple, compile-time class declaration for Perl

Awesome Lists containing this project

README

        

=pod

=encoding UTF-8

=head1 NAME

Class::Slot - Simple, efficient, comple-time class declaration

=head1 VERSION

version 0.09

=head1 SYNOPSIS

package Point;

use Class::Slot;
use Types::Standard -types;

slot x => Int, rw => 1, req => 1;
slot y => Int, rw => 1, req => 1;
slot z => Int, rw => 1, def => 0;

1;

my $p = Point->new(x => 10, y => 20);
$p->x(30); # x is set to 30
$p->y; # 20
$p->z; # 0

=head1 DESCRIPTION

Similar to the L pragma, C declares individual fields in a
class, building a constructor and slot accessor methods.

Although not nearly as full-featured as L L,
C is light-weight, fast, works with basic Perl objects, and
imposes no dependencies outside of the Perl core distribution. Currently, only
the unit tests require non-core packages.

C is intended for use with Perl's bare metal objects. It provides
a simple mechanism for building accessor and constructor code at compile time.

It does I provide inheritance; that is done by setting C<@ISA> or via the
C or C pragmas.

It does I provide method wrappers; that is done with the C
pseudo-class.

It I build a constructor method, C, with support for default and
required slots as keyword arguments and type validation of caller-supplied
values.

It I build accesor methods (reader or combined reader/writer, using the
slot's name) for each slot declared, with support for type validation.

=head1 @SLOTS

The C<@SLOTS> package variable is added to the declaring package and is a list
of quoted slot identifiers. C<@SLOTS> includes I slots available to this
class, including those defined in its ancestors.

=head1 CONSTRUCTOR

C generates a constructor method named C. If there is already
an existing method with that name, it may be overwritten, depending on the
order of execution.

=head1 DECLARING SLOTS

The pragma itself accepts two positional parameters: the slot name and optional
type. The type is validated during construction and in the setter, if the slot
is read-write.

Slot names must be valid perl identifiers suitable for subroutine names. Types
must be either a code ref which returns true for valid values or an instance of
a class that supports the C, C, and C
methods (see L).

The C pragma may be used as either a keyword or a pragma. The following
are equivalent:

use Class::Slot x => Int;
use slot x => Int;
slot x => Int;

A simple source filter is used to translate uses of C and C
into C. This is a somewhat brittle solution to ensuring
compile time code generation while avoiding a clash with
L, which uses the C namespace internally but
nevertheless holds the keys to it on CPAN.

As a result, care must be taken when defining slots using the C
syntax (rather than C). The source filter identifies
the keyword C when it appears as the first value on a line, followed by
a word boundary. There is the potential for false positives, such as with:

my @ots = qw(
slot blot glot clot
);

=head1 OPTIONS

=head2 rw

When true, the accessor method accepts a single parameter to modify the slot
value. If the slot declares a type, the accessor will croak if the new value
does not validate.

=head2 req

When true, this constructor will croak if the slot is missing from the named
parameters passed to the constructor. If the slot also declares a
L value, this attribute is moot.

=head2 def

When present, this value or code ref which returns a value is used as the
default if the slot is missing from the named parameters passed to the
constructor.

If the default is a code ref which generates a value and a type is specified,
note that the code ref will be called during compilation to validate its type
rather than re-validating it with every accessor call.

=head2 fwd

When present, generates delegate accessor methods that forward to a mapped
method on the object stored in the slot. For example:

# Foo.pm
class Foo;

sub life{ 42 }

1;

# Bar.pm
class Bar;
use Class::Slot;
use parent 'Foo';

slot 'foo', fwd => ['life'];

1;

# main.pl
my $bar = Bar->new(foo => Foo->new);
say $bar->life; # calls $bar->foo->life

Alternately, C may be defined as a hash ref mapping new local method
names to method names in the delegate class:

# Bar.pm
class Bar;
use Class::Slot;
use parent 'Foo';

slot 'foo', fwd => {barlife => 'life'};

1;

# main.pl
my $bar = Bar->new(foo => Foo->new);
say $bar->barlife; # calls $bar->foo->life
say $bar->life; # dies: method not found

=head1 INHERITANCE

When a class declares a slot which is also declared in the parent class, the
parent class' settings are overridden. Any options I included in the
overriding class' slot declaration remain in effect in the child class.

package A;
use Class::Slot;

slot 'foo', rw => 1;
slot 'bar', req => 1, rw => 1;

1;

package B;
use Class::Slot;
use parent -norequire, 'A';

slot 'foo', req => 1; # B->foo is req, inherits rw
slot 'bar', rw => 0; # B->bar inherits req, but is no longer rw

1;

=head1 COMPILATION PHASES

=head2 BEGIN

C statements are evaluated by the perl interpreter at the earliest
possible moment. At this time, C is still gathering slot
declarations and the class is not fully assembled.

=head2 CHECK

All slots are assumed to be declared by the C phase. The first slot
declaration adds a C block to the package that installs all generated
accessor methods in the declaring class. This may additionally trigger any
parent classes (identified by C<@ISA>) which are not yet complete.

=head2 RUNTIME

If C is not available (for example, because the class was generated in a
string eval), the generated code will be evaluated at run-time the first time
the class' C method is called.

=head1 DEBUGGING

Adding C to your class will cause C to
print the generated constructor and accessor code just before it is evaluated.

Adding C anywhere will cause C to emit
debug messages globally.

These may be set from the shell with the C environmental
variable.

=head1 PERFORMANCE

C is designed to be fast and have a low overhead. When available,
L is used to generate the class accessors. This applies to
slots that are not writable or are writable but have no declared type.

This behavior can be disabled by setting C<$Class::Slot::XS> to a falsey value,
although this must be done in a C block before declaring any slots, or
by setting the environmental variable C to a truthy value
before the module is loaded.

A minimal benchmark on my admittedly underpowered system compares L,
L, and L. The test includes multiple setters using a mix of
inherited, typed and untyped, attributes, which ammortizes the benefit of
Class::XSAccessor to L and L.

| Rate moo moose slot
| moo 355872/s -- -51% -63%
| moose 719424/s 102% -- -25%
| slot 961538/s 170% 34% --

Oddly, L seemed to perform better running the same test without
L installed.

| Rate moo moose slot
| moo 377358/s -- -50% -56%
| moose 757576/s 101% -- -12%
| slot 862069/s 128% 14% --

=head1 AUTHOR

Jeff Ober

=head1 COPYRIGHT AND LICENSE

This software is copyright (c) 2020 by Jeff Ober.

This is free software; you can redistribute it and/or modify it under
the same terms as the Perl 5 programming language system itself.

=cut