Programming with Moose/Printable version
This is the print version of Programming with Moose You won't see this message or any elements not part of the book's content when you print or preview this page. |
The current, editable version of this book is available in Wikibooks, the open-content textbooks collection, at
https://en.wikibooks.org/wiki/Programming_with_Moose
Preface
The first goal of a book should be to convince the reader it's worth reading in the first five minutes. It shouldn't take you five minutes to read this.
Should I even be reading this?
editThis book is free, and in being free I can willfully tell people how to save time up front: If the following applies to you do not waste your time and go away:
- You don't use the OOP paradigm and you don't care to know what you're missing.
- You won't entertain the idea of making your perl programs dependent on a module that does not exist in the CORE distribution.
- You think all syntax that looks foreign to perl is bad.[1]
- You're the benchmark-before-completion type of programmer, or you won't compromise on execution speed.
- You don't like radical change.
Tell me what you're 'twerkin with
editLet me start out with the problem: perl5 is old and crusty.
The OOP paradigm of Perl5 is in severe need of a face lift. Not only does it lack functionality that other programming languages have, the functionality it does have it does poorly. Moose will scrape off this crust and abstract away these shortfalls. All hail the shiny newer Moose-powered perl.
Moose is an awkward gem, and telling you what it's about is more of a boring practice of pedantry - one we will tackle at a later point. Moose does a lot, and it has ambitious goals, but aside from what it wants to accomplish, you'll find it delivers on the following:
- It will eliminate the need for the variety of modules that help compensate for that aforementioned old and crustyness.
- It will make programming more fun and less tedious. To further assist the hype-catalyst, it will make the fun more fun, and the tedious more fun, and the tedious less existent and.. or something...
- It will provide a versatile framework for which you can quickly prototype, and meld real-world problems.
- It will give you real world practice with another up and coming paradigm: AOP.
Be prepared to leave behind all of your old perl-OOP practices. Moose doesn't play well with them: it single-handedly replaces them! No more need for Class::Accessor
, or writing manual checks using ref()
inside of your app. Gone are the days of using Conway's cursed NEXT
, or violating blackbox rules to get the job done. Tokens, Models, and Events are all that you should be thinking of. Moose will think of the rest for you in a totally impersonal pre-thought-out fashion of course.
One more thing, this book goes about teaching Moose by comparing the "(old) perl way", with the "(new) Moose way." If this analogy bothers you, you can find the non-entertaining Moose docs on CPAN.
There is in addition a set of syntax docs in this book, which are surely more neutral.
Footnotes
edit- ^ Moose is not a source filter. The syntax looks foreign to native perl because the idiom
bareword=>()
is not adopted anywhere in the Perl example. Moose has no voodoo of the sourcefilter sort.
Obtaining Moose
There are numerous way you can obtain the latest version of Moose. Some of the more popular methods are listed below.
CPAN
editUsing the CPAN module:
perl -MCPAN -e"install Moose"
Using the interactive shell, in non-interactive mode:
cpan Moose
To use the shell in interactive mode simply:
cpan
cpan[1]> install Moose
Tarball
editDownload from http://search.cpan.org/dist/Moose/.
tar xzvvf Moose*
cd Moose*
perl Makefile.PL
sudo make install
Debian (and Debian-derived systems)
editIf you're running Debian, or a Debian-based system, your package manager can install Moose. Using this method has advantages and disadvantages. The disadvantage is that you will not always be running the bleeding edge of Moose, and not all Moose extensions are packaged for Debian. The advantages are that you get quality control, security tracking, and ease of installation with Debian's APT.
To install Moose on a Debian system;
apt-get install libmoose-perl
Ideology
Goal
editMoose's goal is to increase programmer productivity by patching suboptimal syntax and Perl5's lacking object model. Moose goes about this by doing the grunt work for you. Nothing Moose accomplishes is done with source filters.
Speed
editMoose does not endeavor to make your program execute faster, nor does it show much concern for compile time. Most of the speed hit for using Moose occurs at compile time by design.
Base-type independence
editOne of the things Moose accomplishes is an abstraction away from Perl5's base data-types. It shouldn't matter if your object is a blessed hash, array, or scalar with Moose and proper practices you can accomplish base-type independence.
The bundle
editA simple use Moose;
should be your one-stop-shop for an off-the-shelf OO solution to both Perl's problems and yours. The latest and greatest sandbox for Moose features too esoteric to be included into Moose, or things built with the framework can be found in the MooseX::
namespace. MooseX is loosely reserved for things which build on top of Moose, or things that modify the behavior of Moose. Note however that MooseX is not the appropriate namespace for code which just uses Moose. Some extensions include MooseX::Traits, MooseX::Singleton, MooseX::Types.
Problems solved
Perl (prior to version 5.10) has an illogical Method Resolution Order (MRO), this becomes apparent when using multiple inheritance dispatching. A fix for MI-dispatching was first popularized with Dameon's NEXT
, which turned out to be a buggy hack that failed horribly in many cases. NEXT
was unfortunately inducted into the CORE distro with Perl v5.7.3 and remains in the CORE even today. Moose uses the newer jazzier C optimized Class::C3
which is the new non-default standard for proper method-dispatch in all Perl's greater than Perl 5.95.[2]
Before Class::C3
and NEXT
, you had SUPER
which predates NEXT
by approximately six months and thankfully never made CORE. SUPER
was goofy and mostly useless because if foo and bar were siblings and subclasses of baz you could not re-dispatch in foo to a method in bar.
Essentially SUPER
supported Method Dispatch only for linear patterns. NEXT
supported them in a fashion that was quirky and made little sense.[3] Without learning the quirks there is a high probability the new Class::C3
does precisely what the programmer wants - it is intuitive. Class::C3
uses the C3 MRO algorithm which is also employed by Python.
The old way
editThe differences between NEXT
and Class::C3
are outside of the range of this book; but, because vanilla Perl doesn't even permit method resolution, and dispatch in the sense of the OO paradigm we will show NEXT
in its stead.
An Example
editpackage A;
use strict;
use warnings;
use NEXT;
sub name {
my $self = shift;
$self->{-name} = shift if @_;
return $self->{-name};
}
sub new {
my $obj = shift;
my $class = ref($obj)?ref($obj) : $obj;
my $args = shift;
my $self = bless {}, $class;
$self->{-name} = $args->{name} if( $args && ref($args) eq 'HASH' && $args->{name});
return $self;
}
sub say_name {
my $self = shift;
print "My name is ".$self->name.".\n";
}
package B
use strict;
use warnings;
use NEXT;
our @ISA = 'A'
package C
use strict;
use NEXT;
our @ISA = 'B'
sub say_name {
my $self = shift;
print "The name given to me is ".$self->name.".\n";
}
package main;
my $a = A->new({ name => 'Bob' });
# prints:
# My name is Bob
$a->say_name;
my $c = C->new({ name => 'Bill' });
#prints:
# The name given to me is Bill
$c->say_name;
Invoking the Moose
editAn Example
edit package A;
use Moose; # Includes strict and warnings
# Define an attribute the object instance has.
# In this case a name, which has a type of string, and
# has a read accessor and a write accessor.
has name => ( isa => 'Str', is => 'rw' );
sub say_name {
my $self = shift;
say " my name is ".$self->name.".\n";
}
no Moose; # Clean up the namespace.
package B;
use Moose;
no Moose;
package C;
use Moose;
sub say_name {
my $self = shift;
say "The name given to me is ".$self->name.".\n";
}
no Moose;
package main;
# We get new for free.
my $a = A->new({ name => 'Bob' });
# prints:
# My name is Bob
$a->say_name;
my $c = C->new({ name => 'Bill' });
#prints:
# The name given to me is Bill
$c->say_name;
External Links
editFootnotes
edit- ^ Perl 5.95 adds a new pragma mro which uses C3 resolution. This pragma is however opt in and makes a few tools available to you, which can all be utilized maintaining reverse compatibility with older Perl, using MRO::Compat.
- ^ The method that made little sense is called Depth First Search (DFS). DFS was the predecessor MRO to C3 in Perl. Briefly, it did not handle any form of triangle inheritance in a sane fashion.
Problems solved/Accessors
Perl has two aggregate (non-scalar) data types: arrays and hashes (i.e., associative arrays) which are commonly used as the base data structure for objects. A more serious problem is that neither of these data types is an object. For instance, native array and hash support doesn't allow you to receive an event trigger when you write a new key-value pair to a hash, or when you push a new element onto an array. It's possible to use Perl's tie() mechanism to get this, but tie() comes with a serious performance penalty. Perl does enhance the C-style array of fixed size - a simple contiguous block of memory, addressed via indexing - to allow it to grow or shrink as needed, but chose for reasons of efficiency to implement only basic operations.[4]
One of the two might be better for one task, and not applicable for another, but that doesn't matter. With Moose, your program isn't directly tied to a Perl data structure.
The old way
editTraditionally, Class::Accessor
, has done the most for arena, we'll see that soon. The problem it solved was a big and simple one, and it didn't overachieve one bit: if you've got a hash and you don't want a key: foobar
to be inserted, how do you go about it? You simply bless
the hash into an object and say what accessors should be generated. Now this "hash" acts as watered down model that your module can wrap to achieve the goals of the program. When an unrelated function is called perl realizes it can't resolve the method to a matching sub and errors.[5]
An Example
editExamine the terseness of this code.
package OldWay;
use strict;
use warnings;
use base 'Class::Accessor'; ## or C::A::Fast, or C::A::More, et al.
BEGIN { Class::Accessor->mk_accessors( qw/ foo bar baz / ) }
sub new {
my $class = shift;
bless {}, $class;
}
package main;
use strict;
use warnings;
my $obj = OldWay->new;
## These work
$obj->foo( 1 );
$obj->bar( OldWay->new );
$obj->baz( 'foo' );
## Anything else will die
$obj->quz( 1 ); #die
Here we create an object, essentially a hash that permits only three keys. The getters and setters for this hash directly correlate to the keys in the underlying perl hash. Remember, that a Perl object is very simple. complexities are left as an exercise to the programmer.
Invoking the Moose
editTruth be told, Moose is more than a replacement for Class::Accessor
. So, off the shelf, it won't compete with the simplicity of Class::Accessor
. In this section however, we will see Moose's analog.[6]
An Example
editpackage NewWay;
use Moose;
has 'foo' => ( isa => 'Value', is => 'rw' );
has 'bar' => ( isa => 'Value', is => 'rw' );
has 'baz' => ( isa => 'Value', is => 'rw' );
# or just
# has [qw/ foo bar baz /] => ( isa => 'Value', is => 'rw' );
package main;
use strict;
use warnings;
my $obj = NewWay->new;
## Rest of the module is the same..
## These work
$obj->foo( 1 );
$obj->bar( NewWay->new );
$obj->baz( 'foo' );
## Anything else will die
$obj->quz( 1 ); #die
Pretty simple. No syntax thus far really needs explaining -- but in the next section we will anyway.
New Syntax
editFor clarity and those who just can't learn by example:
has
edithas is one of few Moose keywords, it begins a Moose expression that declares a statement.
isa
editisa in the context of has refers to the TypeConstraint of the object. Moose has a built in type system, and this keyword, which will be explained further in the next chapter specifies what values this attribute is to accept.
is
editis in the context of has refers to the type of accessors to be created. A value of rw will instruct Moose to forge a setter and getter, while a value of ro will only yield a get'er.
Footnotes
edit- ^ Unless of course you have explicitly crafted a
sub AUTOLOAD {}
, let's not be bothered with this exception though. - ^ By fixed we mean you can push onto both sides of the array. There is no guarantee new data will be contiguous to the old data. When you program with perl you aren't supposed to think at a low level, don't worry about what happens just know it'll work.
- ^ Of course, with
Moose::Tiny
you will probably get a more powerful and more terse alternative toClass::Accessor
but we will addressMooseX::
and alternatives at a later point in this book.
Problems solved/Type-system
Perl lacks and Moose doesn't. At least, not nearly as much.
Back to the issues of Perl, more specifically another feature not covered by the Object System but delivered as a blessed benediction of Moose. So in this example, you're writing a program and like a good programmer, *all* of your URLs are handled by URI
objects, and all of your methods operate on URI
objects - or at least they think they do. Having methods dependent on URI
objects is a good thing. Trust me, it is, the alternative is heaven-1. Unfortunately, when you publish the module to a world of intellectually inferior blokes you have no streamlined method of making sure they send only URIs.[7]
Again this is where Perl is lacking. All urls are stored as URI
objects, but you can't easily make sure they're only URI
objects.
Roots in pre-object fanaticsm
editPonder for a bit the utility of sigils. One of the side effects of sigils is a function knows at compile time whether or not the input sent to it is in the right ballpark. Perl's sigils are a little more voodoolicious because sometimes they can change the value by acting as an operator.[8] Let's look at how perl deals with validating input.
my %foo = ( foo => bar );
# This will not compile because the CORE::values function
# only operates on a hash, and not an array as indicated
# by the sigil. See perl's prototypes for more info.
print for values @foo;
In the following example, the class HashOperations expects a HashObject:
my $foo = HashObject->new;
$foo = 'stupid_mistake';
## Stupid non-intelligent error will happen here
## because the user made a stupid mistake.
HashOperations->load( $foo )->values;
So the issue here, is that the procedural non-referenced form will be caught in compile time, and all other object variants will not.
The old way
editThe old stock-Perl way to tackle this problem was really a joke on the users. Rather than adding specific functionality to the CORE, or radically modifying it, the core-team would rather bother the programmer with modular language add-ons that productivity is totally dependent on.[9] We can speculate on the reasoning for the lack of TypeConstraint functionality.
- There weren't any examples of interpreted languages for Perl to model itself after, never mind interpreted languages with base-object types.
- Perl's references came before its objects.
- A hash is a Hash (of Scalars), an array is an Array (of Scalars). A good portion of this can be determined on compile time by the use of perl's sigils. This is not so with Objects which occur wholly at runtime, and aren't granted a unique sigil.
An Example
editIn this set of examples we will show how Class::Accessor is automagically inferior to Moose at making accessors, because of Moose's ability to specify valid types.[10]
Here we create a custom datatype, ie. NonMooseObject:
package NonMooseObject;
use strict;
use warnings;
use base 'Class::Accessor';
BEGIN { __PACKAGE__->mk_accessors( 'uri' ) };
sub new {
my ( $class, $hash ) = @_;
my $self = bless $hash || {}, $class;
$self;
}
This is what you want your user to do:
package main;
my $uri = URI->new( 'http://moose.com' );
my $obj = NonMooseObject->new({ uri => $uri });
$obj->uri; ## prints php.
$obj->uri->path; ## joy
This is what you don't want your user to do. In this example we show how easily a NonMooseObject can be abused. There is one important thing that is difficult to show in an example: the death might or might not occur on the call to ->new
. Hopefully, for the user and programmer, it dies on the call to new; but, let's assume you run for 42 days, and then something internally calls ->path
on $obj->uri
when ->uri
does not contain a URI object. The result is then a bug that could be much harder to troubleshoot.
package main;
my $obj = NonMooseObject->new({ uri => 'http://perl.com' });
$obj->uri; ## prints php.
$obj->uri->path; ## dies a horrid runtime death.
The pre-Moose days
editQuestion | How did you solve this problem in Perl's pre-Moose era |
---|---|
Answer | We downgraded, losing the utility of Class::Accessor .
|
We told ourselves this functionality, is more advanced and niche, isn't needed everywhere. To accomplish this functionally you much first decouple your module and C::A
. So not just is this solution ugly, but now you're back at having to write accessors. Baby Jesus cried around this point in time.
sub set_stupid {
my ( $self, $uri ) = @_;
die "bad uri" unless ref $uri eq 'URI';
$self->{uri} = $uri;
}
If you finding yourself using ref after the completion of this book, you need to swiftly stop programming and find yourself a new profession. |
Invoking the Moose
editDredging up the past is boring and disturbing, so let's now look forward now, towards the light.
An Example
editpackage MooseObject;
use Moose;
has 'uri' => ( is => 'rw', isa => 'URI', required => 1 );
No more examples needed. You now either send in a URI
object to the constructor, or you die with an accompanying stack trace and Moose's default message telling you what you need to do.
Footnotes
edit- ^ This is just as much of an issue if you expect others to follow the principles of blackbox design. The blackbox design states you should not muck with a module's internal, ie. non-exposed, non-documented, functions. With Moose, you can easily define more of what your function does so if they decide to muck with it their job will be easier.
- ^ @array says 'array' is an array. Yet @$array, says dereference $array as an array. Thus '@' will either imply the datatype is an array, or it points to one and it should be coerced via dereferencing.
- ^ Don't be an annoying kid and point out that a TypeConstraint has nothing to do with an accessor, and if you call it bloat then my stupid-seeking missile will kill you.
- ^ We are of course referring to the productivity when utilizing the object oriented paradigm of perl.
Problems solved/Scalar-Defer
Scalar::Defer
's lazy
is a method of computing a value once and caching it away, while Memoize is a method of storing the functions input and output into a cache table for the purpose of future calls. Both of these functionalities are largely covered by Moose's native lazy=>1, default=>
. This deadly combo (lazy
and default
) native to Moose caches a function's output, and delays the execution until the first runtime request.
Procedurally, a function gets arguments or operates on globals -- if it does neither than it is presumably immutable[11]. Object Orientationally, a method[12] often gets its variables from the object (its environment) and sometimes modifies the object. Take for instance a method like ->shout
, which might check to see if the object can shout()
and then it might shout what the object is $self->thinking
. With this in mind, let's see what Memoize does...
‘Memoizing’ a function makes it faster by trading space for time. It does this by caching the return values of the function in a table. If you call the function again with the same arguments, "memoize" jumps in and gives you the value out of the table, instead of letting the function compute the value all over again.
You might ask, what's wrong with that? Well, for starters it isn't object oriented[13]; and, doing anything with a method reference is ugly and nasty and should be avoided. Moose's Lazy/Default combo replaces a small but significant subset of Memoize's functionality. If you simply want to cache the return of a runtime function then Moose's native functionality might better suite you.
Finally, the purpose of Memoize is always to make something faster and that isn't Moose's goal in any way shape or form. What functionality Moose delivers with lazy
/default
is for a totally different reason, all of which more so pertains to programmer productivity which is exactly the goal of Moose. The logical drawback of the way Moose goes about this is that the object will cache only one call to the function, requiring many objects for many calls.
As for the Scalar::Defer
, this probably better fits the functionality Moose provides, because the goal of Scalar::Defer
, directly collides with that of Moose's Lazy/Default. Let's sum up with a list of things the Lazy/Default combo can be said to do.
- Calculates the function only once
- Caches the output
- Defers evaluation until the first call
The old way
editI've (Evan Carroll) have personally never seen memoize used before on a method. Not to say it couldn't be done, or hasn't been... But, rather than contrive an example to make Moose more of the win, I'll present you with a typical vanilla Memoize. That goes to say this could be made more similar in theory, but not in practice.
This example will use lazy/default for the purpose of delaying execution and storing the result; it is often used simply for this. However, it is also often used for delaying a database connection until needed in runtime. These are two totally separate applications of the same technology.
An Example (memoize)
edituse Memoize;
memoize( 'complex_operation' );
sub complex_operation {
my $num = shift;
my $result = $num+$num+$num+$num+$num+$num+$num
+$num+$num+$num+$num+$num+$num+$num
+$num+$num+$num+$num+$num+$num+$num
+$num+$num+$num+$num+$num+$num+$num
+$num+$num+$num+$num+$num+$num+$num
+$num+$num+$num+$num+$num+$num+$num
;
}
print complex_operation( 1 );
Invoking the Moose
editMoose is going to get the same effect a slightly different — object oriented — way.
An Example (lazy/default)
editpackage MyMoose;
use Moose;
has 'foo_var' => (
isa => 'Int'
, is => 'ro'
);
has 'foo' => (
isa => 'Int'
, is => 'rw'
, lazy => 1
, default => \&build_foo
);
sub build_foo {
my $self = shift;
my $num = $self->foo_var;
my $result = $num+$num+$num+$num+$num+$num+$num
+$num+$num+$num+$num+$num+$num+$num
+$num+$num+$num+$num+$num+$num+$num
+$num+$num+$num+$num+$num+$num+$num
+$num+$num+$num+$num+$num+$num+$num
+$num+$num+$num+$num+$num+$num+$num
;
}
package main;
my $m = MyMoose->new({ foo_var => 1 });
print $m->foo;
Another Example
editI feel obligated to show you a very popular application of this technology.
package MyApp::DB;
use Moose;
use DBI;
has 'dbh' => (
isa => 'Object'
, is => 'ro'
, lazy => 1
, default => \&build_dbh
);
sub build_dbh {
my $self = shift;
my $dbh = DBI->connect(
'dbi:Pg:dbname=myDB;host=localhost'
, 'username'
, 'password'
) or die "Can not connect to DB $DBI::errstr";
$dbh;
}
New Syntax
editFor clarity and those who just can't learn by example:
- lazy
- Lazy can be reduced to a simple: do this at runtime. It permits delayed execution.
- default
- Default is always used in combination with lazy, tells Moose what should happen when the first call to the getter is made. The combination of lazy/default essentially does a few things:
- Delays execution until runtime
- Caches away variable for later calls
- Permits dynamic overload
So if you don't want to the default/lazy combo, just send it something.
References
editMemoize v1.01: POD. Date 2001-09-21. Access date 2007-10-21
Footnotes
edit- ^ An immutable function simply means for every X you will get the same Y. All functions are ispo facto immutable if they have no variables (X) to influence their output (Y). If you have to think about this you should probably retire from programming.
- ^ A method is simply a function in the objects namespace.
- ^ Not everything has to be object oriented! This isn't java; we support multiple paradigms in our language, and we know that.
- ^ So you can violate this immutability by calling the ->clearer, we will talk about this later.
Evolution of the Moose
Your typical non-Moose program will undergo a series of changes as your understanding of Moose advances and you achieve the venerable rank of Moose elder:
- Dismantling the
sub new {}
- Breaking apart the setters by using
sub BUILD {}
- Removing
sub new {}
,sub BUILD {}
Your end result will be an application which delays most tasks until called for. You will find less of a need to use sub new {}
because the very nature of it is more compile time than you will often require. That is to say, Moose will permit you to further fragment your monolithic new, into smaller tinier more manageable news that work when you try to retrieve information. This doesn't completely kill off the vanilla sub new {}
, but it knocks off a big chuck of it.
Roles and Plugins
14:00 <@mst> EvanCarroll: add a disclaimer saying "concepts may be oversimplified and this has not been audited by the Moose developers so may not always be precisely correct" 14:00 <@mst> then stevan can audit it and give a page the seal of approval if he wants to 14:00 <@mst> and not if he doesn't 14:00 < EvanCarroll> mst: sounds fair enough 14:00 <@mst> EvanCarroll: "Only a class can be subclassed, but there's no reason you can't subclass a class with a bunch of roles applied to it - also, see <link to stuff on role composition>"
Qualifying scenarios for roles
edit- SFTHM
- Straight From The Horses Mouth
- If you can model your problem without using Roles, and with only single inheritance do it.
- Anything that can modeled with Roles can be modeled with Multiple Inheritance.[14]
- If you know at compile time you want to apply a plugin, you probably don't wan't to use a plugin.[15]
- If a plugin or role is only used one class, you're better off not using it.
- A role should only specify methods, and not subs. Subs should be specified in a module that uses Sub::Exporter instead of Moose's role system. A role is for method composition. Roles do more than just export. SFTHM
Order sensitivity
editRoles try not to be order sensitive when it comes to requirements, and conflict resolution; however, they are highly order sensitive when it comes to composition.
The order the roles are used determines the order of the method modifiers. Any method modifiers in the class before the with (keyword that includes the roles) will run before them, and any that are declared after the with will run after the roles.
Disadvantages to inheritance
edit- ^ Inheritance permits build time setting of args via the constructor, i.e,
Foo->new({bar=>'baz'})
. If Foo is a subclass of Bar and Bar provides a few attributes viaMoose#has
, those attributes can be set in Foo's constructor arguments. In the plugin model, Foo applies plugin Bar and then you only set that which Bar does via one method call per attribute, rather than by hash. There is no way to apply Bar, and then initialize Foo with Bar's modifications using one hash, nor is there a way to initialize just Bar's modifications using one hash.- All compile time resolution is disabled, i.e, +has => ( default => 'NewDefault' ) has no effect in a plugin.
- ^ You can not subclass a Role: a role for instance can provide contact information on a person, afterward it is said that a person can be contacted. A role however can not subclass that role to provide US-Specific contact information, for this you either must use two roles, or multiple-inheritance.
- Some tools are not supported by Roles, such as Moose's augment.
Type system
Gotchas
edit- Overloading
- Maybe[Foo] 'Foo | Undef': One permits coercing to Undef, one doesn't.
13:42 <@mst> EvanCarroll: you can do a coercion on "Maybe[Foo]"
Syntax
A big problem with the CPAN docs thus far is they utilize (abuse) the fact that Moose is an application of Class::MOP
. Class::MOP
is Meta Object Protocol building framework - and that is usually about where the cryptic confusion sets in.. There is a very blurry distinction between Class::MOP
and Moose, because Moose is the only known application of Class::MOP
.
Essentially what we have in Class::MOP
is an archive of hacks that allow Moose to be terse and non-pearlish. Moose makes a few of these hacks available with out much sugar but generally speaking candy coats them to a comfortable level.
In this syntax compendium of Moose we promise to never mention Class::MOP
again.[16]
Table of Contents
editKeywords exported with use Moose;
:
- has
- before, after, around
- blessed
- override
- augment
- extends
- with
- meta
- Other functions - Non-polluting fully-qualified syntax
Use of 'no Moose'
edit15:32 <@konobi> less shit to track during runtime
15:33 <@Sartak> EvanCarroll: my $person = Person->new; $person->has("jewelry")
15:33 <@konobi> just _having_ stuff in your namespace will slow the interpreter down
15:33 <@Sartak> if you keep Moose's functions in Person's namespace, that will have weird results
15:33 <@Sartak> if you no Moose, then it's the usual "undefined function" error
Footnotes
edit^ Class::MOP
, last time, I swear.
Difficulties/Moose-w-Moose
After using Moose for a while you will eventually find yourself with an assortment of classes that you wish to reuse. Unfortunately, Moose won't make it all that easy to use those Moose classes from within Moose. Here are some difficult tasks[17] to do within Moose:
In these examples, MooseObj, is a Moose object.
- isa => ArrayRef[ MooseObj ]
- There is no syntactic sugar to facilitate in creating MooseObj(s).
- Not easily possible to coerce from
$self->MooseObj( [{constructor_args1}, {constructor_args2}] )
.
- isa => 'MooseObj'
- No syntactic Autoboxing of the MooseObj, you can't do the following:
$self->foo->new( {constructor_args} )
. - No implicit way to bypass the creation of MooseObj and set its accessors. more on this in One-to-one Auto-vivification.
- No syntactic Autoboxing of the MooseObj, you can't do the following:
One-to-one Auto-vivification
editWhat if you don't care about MooseObj, only the accessors inside? A child object might do no more than function as logical grouping for more accessors, as in the following example:
package Address;
use Moose;
has 'street' => ( isa => 'Str', required => 1, is => 'ro' );
has 'city' => ( isa => 'Str', required => 1, is => 'ro' );
has 'zip' => ( isa => 'Str', required => 1, is => 'ro' );
has 'state' => ( isa => 'Str', required => 1, is => 'ro' );
package USProfile;
use Moose;
has 'workAddress' => ( isa => 'Address', is => 'ro' );
has 'homeAddress' => ( isa => 'Address', is => 'ro' );
Even though Moose very well seeks to replace and abstract away a lot of perl's base-types here in comparison to having an Address hash it makes the job difficult. Here are some of the obvious difficulties:
- You can not skip the step of creating a new Address object
- The syntax
$USProfile->workAddress->street('1234 FooStreet')
, is not valid - The syntax
$USProfile->workAddress->{street} = '1324 FooStreet'
would be (assuming workAddress was a hash.)
- The syntax
- You might wish for the component-class Address to require its child-attributes, but there is no way to later disable those
requires=>1
, when the class is used as a child of another class.- The syntax
has 'workAddress' => ( isa => 'Address', is => 'ro', default => sub{Address->new} );
is not valid. - This wouldn't be all that useful in this case because of the like-problem with
is=>'ro'
.
- The syntax
Proposed Solution
editTHIS SECTION IS BEING RE-WRITTEN Now obviously something nifty should happen to facilitate the creation of email. Any work done here would do a lot to further Moose's application into a Token-based system. Such as XML, where you can easily model, a one level tree in Moose, and then pull that into to another Moose class for a multiple level document. The following is pseudo-code that is not implemented.
my $person = USProfile->new;
## Hack a handles new into the Email code, which calls its own constructor
my $email = $person->email->new({ (constructor) })
## Still permit some sort of construction that suffices the required
## obviously a no-go if you have it embedded in an ArrayRef or HashRef
my $person => USProfile->new({state=>'TX', email=>{[moose_args=>]constructor args} }
Maybe there needs to be a set of AttributeHelpers for things that inherit from Moose::Object
Another way to accomplish this would be something like (provided by steven)
has hash => (
is => "ro"
, default => sub { blessed(shift)->new }
, lazy => 1
);
my $h = Hash->new;
$h->hash->hash->hash;
print $h->dump;'
There are a few issues with this:
blessed(shift)->new
should be the value theisa=>
instead.- It doesn't accommodate for
isa
's, that haverequired=>1
, there must be some way to autovivify, or send constructor args.
Footnotes
edit- ^ Difficult or overly verbose.