NAME
Moxie - Not Another Moose Clone
VERSION
version 0.07
SYNOPSIS
package Point {
use Moxie;
extends 'Moxie::Object';
has x => ( default => sub { 0 } );
has y => ( default => sub { 0 } );
sub x : ro;
sub y : ro;
sub clear ($self) {
$self->@{ 'x', 'y' } = (0, 0);
}
}
package Point3D {
use Moxie;
extends 'Point';
has z => ( default => sub { 0 } );
sub z : ro;
sub clear ($self) {
$self->next::method;
$self->{z} = 0;
}
}
DESCRIPTION
Moxie is a new object system for Perl 5 that aims to be a successor to the Moose module. The goal is to provide the same key features of Moose; syntactic sugar, common base class, slot management, role composition, accessor generation and a meta-object protocol – but to do it in a more straightforward and resource efficient way that requires lower cognative overhead.
The key tenents of Moxie are as follows:
Aims to be ultra-modern
Moose was a post-modern object system, so what is after post modernism? Post, post modernism? Who knows, it is 2017 and instead of flying cars we are careening towards a dystopian timeline and a future that none of us can forsee. So given that, ultra seemed to work as well as anything else.
This tenent means that we will not shy away from new Perl features and we have core a commitment to helping to push the language forward.
Better distinction between public & private
The clean sepeartion of the public and private interfaces of your class is key to maintaining good encapsulation. This is one of the key features required for writing robust and reusable software that can resist the abuses of fellow programmers and still retain it's usefulness over time.
Re-use existing Perl features
Perl is a large language with many features, some of which are useful and some – I believe – people just haven't found a good use for yet. Moxie aims to use as many existing native features in Perl when possible. This can be seen as just another facet of the commitment to modernity mentioned above, ... it is not old, it is retro!
Reduce cognative burdon of the MOP
The Meta-Object protocol that powered all the Moose features was large, complex and difficult to understand unless you were willing to put in the cognative investment. Because the MOP was the primary means of extension for Moose, this meant it was not optional if you wanted to extend Moose. Moxie instead turns the tables, such that it has multiple means of extension, most (but not all) of which are empowered by the MOP. This means that an understanding of the MOP is no longer required to extend Moxie, but when needed the full power of a MOP is available.
Better resource usage
Moose is famous for it's high startup overhead and heavy memory usage. These were consequences of the way in which Moose was implemented. With Moxie we instead try to do the least amount of work possible so as to introduce the least amount of overhead.
KEYWORDS
Moxie exports a few keywords using the BEGIN::Lift module described above. These keywords are responsible for setting the correct state in the current package such that it conforms to the expectations of the UNIVERSAL::Object and MOP modules.
All of these keywords are executed during the BEGIN
phase, and the keywords themselves are removed in the UNITCHECK
phase. This prevents them from being mistaken as methods by both perl and the MOP.
extends @superclasses
-
This creates an inheritance relationship between the current class and the classes listed in
@superclasses
.If this is called, Moxie will assume you are a building a class, otherwise it will assume you are building a role. For the most part, you don't need to care about the difference.
This will populate the
@ISA
variable in the current package. with @roles
-
This sets up a role relationship between the current class or role and the roles listed in
@roles
.This will cause Moxie to compose the
@roles
into the current class or role during the nextUNITCHECK
phase.This will populate the
@DOES
variable in the current package. has $name => sub { $default_value }
-
This creates a new slot in the current class or role, with
$name
being the name of the slot and a subroutine which, when called, returns the$default_value
for that slot.This will populate the
%HAS
variable in the current package.
METHOD TRAITS
It is possible to have Moxie load your Method::Traits providers, this is done when use
ing Moxie like this:
use Moxie traits => [ 'My::Trait::Provider', ... ];
By default Moxie will enable the Moxie::Traits::Provider module to supply this set of traits for use in Moxie classes.
Some traits below are listed as experimental, in order to enable those traits the string :experimental
(with the leading colon) must appear in your traits list.
use Moxie traits => [ ':experimental' ];
# or
use Moxie traits => [ 'My::Trait::Provider', ..., ':experimental' ];
A word about slot names and method trait syntax
The way perl
parses CODE
attributes is that everything within the ()
is just passed onto your code for parsing. This means that it is not neccesary to quote slot names within the argument list of a trait, and all examples (eventually) will confrom to this syntax. This is a matter of choice, do as you prefer, but I promise you there is no additional safety or certainty you get from quoting slot names in trait arguments.
CONSTRUCTOR TRAITS
strict( arg_key => slot_name, ... )
-
This is a trait that is exclusively applied to the
BUILDARGS
method. This is a means for generating a strict interface for theBUILDARGS
method that will map a set of constructor parameters to a set of given slots, this is useful for maintaining encapsulation for things like a private slot with a different public name.# declare a slot with a private name has _bar => sub {}; # map the `foo` key to the `_bar` slot sub BUILDARGS : strict( foo => _bar );
All other parameters will be rejected and an exception thrown. If you wish to have an optional parameter, simply follow the parameter name with a question mark, like so:
# declare a slot with a private name has _bar => sub {}; # the `foo` key is optional, but if # given, will store in the `_bar` slot sub BUILDARGS : strict( foo? => _bar );
If you wish to accept parameters for your superclass's constructor but do not want to specify storage location because of encapsulation concerns, simply use the
super
designator, like so:# map the `foo` key to the local `_bar` slot # with the `bar` key, let the superclass decide ... sub BUILDARGS : strict( foo => _bar, bar => super(bar) );
If you wish to have a constructor that accepts no parameters at all, then simply do this.
sub BUILDARGS : strict;
And the constructor will throw an exception if any arguments at all are passed in.
ACCESSOR TRAITS
ro( ?$slot_name )
-
This will generate a simple read-only accessor for a slot. The
$slot_name
can optionally be specified, otherwise it will use the name of the method that the trait is being applied to.sub foo : ro; sub foo : ro(_foo);
If the method name is prefixed with
get_
, then this trait will infer that the slot name intended is the remainder of the method's name, minus theget_
prefix, such that this:sub get_foo : ro;
Is the equivalent of writing this:
sub get_foo : ro(foo);
rw( ?$slot_name )
-
This will generate a simple read-write accessor for a slot. The
$slot_name
can optionally be specified, otherwise it will use the name of the method that the trait is being applied to.sub foo : rw; sub foo : rw(_foo);
If the method name is prefixed with
set_
, then this trait will infer that the slot name intended is the remainder of the method's name, minus theset_
prefix, such that this:sub set_foo : ro;
Is the equivalent of writing this:
sub set_foo : ro(foo);
wo( ?$slot_name )
-
This will generate a simple write-only accessor for a slot. The
$slot_name
can optionally be specified, otherwise it will use the name of the method that the trait is being applied to.sub foo : wo; sub foo : wo(_foo);
If the method name is prefixed with
set_
, then this trait will infer that the slot name intended is the remainder of the method's name, minus theset_
prefix, such that this:sub set_foo : ro;
Is the equivalent of writing this:
sub set_foo : ro(foo);
predicate( ?$slot_name )
-
This will generate a simple predicate method for a slot. The
$slot_name
can optionally be specified, otherwise it will use the name of the method that the trait is being applied to.sub foo : predicate; sub foo : predicate(_foo);
If the method name is prefixed with
has_
, then this trait will infer that the slot name intended is the remainder of the method's name, minus thehas_
prefix, such that this:sub has_foo : ro;
Is the equivalent of writing this:
sub has_foo : ro(foo);
clearer( ?$slot_name )
-
This will generate a simple clearing method for a slot. The
$slot_name
can optionally be specified, otherwise it will use the name of the method that the trait is being applied to.sub foo : clearer; sub foo : clearer(_foo);
If the method name is prefixed with
clear_
, then this trait will infer that the slot name intended is the remainder of the method's name, minus theclear_
prefix, such that this:sub clear_foo : ro;
Is the equivalent of writing this:
sub clear_foo : ro(foo);
EXPERIMENTAL TRAITS
In order to enable these traits, you must pass the experimental
flag for Moxie. The interfaces to these traits may change until we settle upon one we like, use them bravely and/or sparingly.
lazy( ?$slot_name ) { ... }
-
This will transform the associated subroutine into a lazy read-only accessor for a slot. The body of the subroutine is expected to be the initializer for the slot and will receive the instance as it's first arguemnt. The
$slot_name
can optionally be specified, otherwise it will use the name of the method that the trait is being applied to.sub foo : lazy { ... } sub foo : lazy(_foo) { ... }
handles( $slot_name->$delegate_method )
-
This will generate a simple delegate method for a slot. The
$slot_name
and$delegate_method
, seperated by an arrow (->
), must be specified or an exception is thrown.sub foobar : handles(foo->bar);
No attempt will be made to verify that the value stored in
$slot_name
is an object, or that it responds to the$delegate_method
specified, this is the responsibility of the writer of the class. private( ?$slot_name )
-
This will generate a private read-write accessor for a slot. The
$slot_name
can optionally be specified, otherwise it will use the name of the method that the trait is being applied to.my sub foo : private; my sub foo : private(_foo);
The privacy is accomplished via the use of a lexical method, this means that the method is not availble outside of the package scope and is not available to participate in method dispatch, however it does know the current invocant, so there is no need to pass that in. This results in code that looks like this:
sub my_method ($self, @stuff) { # simple access ... my $foo = foo; # passing to other methods ... $self->do_something_with_foo( foo ); # calling methods on an embedded object ... foo->call_method_on_foo(); }
USED MODULES
Moxie could be thought of as a reference implementation for an object system built on top of a set of modules (listed below).
- UNIVERSAL::Object
-
This is the suggested base class (through Moxie::Object) for all Moxie classes.
- MOP
-
This provides an API to Classes, Roles, Methods and Slots, which is used by many elements within this module.
- BEGIN::Lift
-
This module is used to create three new keywords;
extends
,with
andhas
. These keywords are executed during compile time and just make calls to the MOP to affect the class being built. - Method::Traits
-
This module is used to handle the method traits which are used mostly for method generation (accessors, predicates, etc.).
FEATURES ENABLED
This module enabled a number of features in Perl which are currently considered experimental, see the experimental module for more information.
signatures
postderef
postderef_qq
current_sub
lexical_subs
say
state
refaliasing
declared_refs
PRAGMAS ENABLED
We enabled both the strict and warnings pragmas, but we disable the reserved
warning so that we can use lowercase CODE attributes with Method::Traits.
AUTHOR
Stevan Little <stevan@cpan.org>
COPYRIGHT AND LICENSE
This software is copyright (c) 2017 by Stevan Little.
This is free software; you can redistribute it and/or modify it under the same terms as the Perl 5 programming language system itself.
2 POD Errors
The following errors were encountered while parsing the POD:
- Around line 230:
Non-ASCII character seen before =encoding in '– but'. Assuming UTF-8
- Around line 402:
You forgot a '=back' before '=head2'