recipe touchups
[gitmo/Moose.git] / lib / Moose / Cookbook / Recipe3.pod
CommitLineData
471c4f09 1
2=pod
3
4=head1 NAME
5
3824830b 6Moose::Cookbook::Recipe3 - A lazy B<BinaryTree> example
471c4f09 7
8=head1 SYNOPSIS
9
10 package BinaryTree;
471c4f09 11 use Moose;
12
8597d950 13 has 'node' => (is => 'rw', isa => 'Any');
14
471c4f09 15 has 'parent' => (
8597d950 16 is => 'rw',
17 isa => 'BinaryTree',
471c4f09 18 predicate => 'has_parent',
8597d950 19 weak_ref => 1,
471c4f09 20 );
21
22 has 'left' => (
8597d950 23 is => 'rw',
24 isa => 'BinaryTree',
7c6cacb4 25 predicate => 'has_left',
26 lazy => 1,
27 default => sub { BinaryTree->new(parent => $_[0]) },
471c4f09 28 );
29
30 has 'right' => (
8597d950 31 is => 'rw',
32 isa => 'BinaryTree',
7c6cacb4 33 predicate => 'has_right',
34 lazy => 1,
35 default => sub { BinaryTree->new(parent => $_[0]) },
471c4f09 36 );
37
38 before 'right', 'left' => sub {
39 my ($self, $tree) = @_;
40 $tree->parent($self) if defined $tree;
41 };
42
43=head1 DESCRIPTION
44
8597d950 45In this recipe we take a closer look at attributes, and see how
46some of their more advanced features can be used to create fairly
47complex behaviors.
48
49The class in this recipe is a classic binary tree, each node in the
50tree is represented by an instance of the B<BinaryTree> class. Each
51instance has a C<node> slot to hold an abitrary value, a C<right>
52slot to hold the right node, a C<left> slot to hold the left node,
53and finally a C<parent> slot to hold a reference back up the tree.
54
e08c54f5 55Now, let's start with the code, our first attribute is the C<node>
8597d950 56slot, defined as such:
57
58 has 'node' => (is => 'rw', isa => 'Any');
59
f7522f24 60If you recall from the previous recipies, this slot will have a
8597d950 61read/write accessor generated for it, and has a type constraint on it.
62The new item here is the type constraint of C<Any>. In the type
63constraint heirarchy in L<Moose::Utils::TypeConstraints>, the C<Any>
64constraint is the "root" of the hierarchy. It means exactly what it
e08c54f5 65says, it allows anything to pass. Now, you could just as easily have
66left out the C<isa>, left the C<node> slot unconstrained and gotten the
67same behavior. But here, we are really including the type constraint
8597d950 68for the benefit of other programmers, not the computer. It makes
69clear my intent that the C<node> can be of any type, and that the
70class is a polymorphic container. Next, lets move onto the C<parent>
e08c54f5 71slot.
8597d950 72
73 has 'parent' => (
74 is => 'rw',
75 isa => 'BinaryTree',
76 predicate => 'has_parent',
77 weak_ref => 1,
78 );
79
80As you already know from reading the previous recipes, this code
e08c54f5 81tells you that C<parent> gets a read/write accessor and is constrained
8597d950 82to only accept instances of B<BinaryTree>. You will of course remember
83from the second recipe that the C<BinaryTree> type constraint is
84automatically created for us by Moose.
85
86The next attribute option is new though, the C<predicate> option.
87This option creates a method, which can be used to check to see if
88a given slot (in this case C<parent>) has a defined value in it. In
89this case it will create a method called C<has_parent>. Quite simple,
90and also quite handy too.
91
92This brings us to our last attribute, and also a new one. Since the
93C<parent> is a circular reference (the tree in C<parent> should
94already have a reference in either it's C<left> or C<right> nodes),
95we want to make sure that it is also a weakened reference to avoid
96memory leaks. The C<weak_ref> attribute option will do just that,
97C<weak_ref> simply takes a boolean value (C<1> or C<0>) and it will
98then add the extra capability to the accessor function to weaken
99the reference of any value stored in the C<parent> slot (1).
100
101Now, onto the C<left> and C<right> attributes. They are essentially
102the same things, only with different names, so I will just describe
103one here.
104
105 has 'left' => (
106 is => 'rw',
107 isa => 'BinaryTree',
108 predicate => 'has_left',
109 lazy => 1,
110 default => sub { BinaryTree->new(parent => $_[0]) },
111 );
112
e08c54f5 113You already know what the C<is>, C<isa> and C<predicate> options
8597d950 114do, but now we have two more new options. These two options are
115actually linked together, in fact, you cannot use the C<lazy>
116option unless you have set the C<default> option. The class
117creation will fail with an exception (2).
118
119Before I go into detail about how C<lazy> works, let me first
120explain how C<default> works, and in particular why it is wrapped
121in a CODE ref.
122
123In the second recipe the B<BankAccount>'s C<balance> slot had a
124default value of C<0>. Since Perl will copy strings and numbers
125by value, this was all we had to say. But for any other item
266fb1a5 126(ARRAY ref, HASH ref, object instance, etc) you would need to
127wrap this into a CODE reference, so this:
8597d950 128
129 has 'foo' => (is => 'rw', default => []);
130
266fb1a5 131is actually illegal in Moose. Instead, what you really want is
132to do this:
8597d950 133
134 has 'foo' => (is => 'rw', default => sub { [] });
135
136This assures that each instance of this class will get it's own
137ARRAY ref in the C<foo> slot.
138
139One other feature of the sub ref version of the C<default> option
140is that when the subroutine is executed (to get back the expected
141default value), we also pass in the instance where the slot will
142be stored. This added feature can come in quite handy at times, as
143is illustrated above, with this code:
144
e08c54f5 145 default => sub { BinaryTree->new(parent => $_[0]) },
146
8597d950 147The default value being generated is a new C<BinaryTree> instance
148for the C<left> (or C<right>) slot. Here we set up the parental
149relationship by passing the current instance to the constructor.
150
151Now, before we go on to the C<lazy> option, I want you to think
152for a moment. When an instance of this class is created, and the
153slots are being initialized, the "normal" behavior would be for
154the C<left> and C<right> slots to be populated with a new instance
155of B<BinaryTree>. In creating that instance of the C<left> or
156C<right> slots, we would need to create new instances to populate
157the C<left> and C<right> slots of I<those> instances. This would
158continue in an I<infinitely recursive spiral of death> until you had
159exhausted all available memory on your machine.
160
161This is, of course, not good :)
162
163Which brings us to the C<lazy> attribute option. The C<lazy> option
164does just what it says. It lazily initializes the slot within the
165instance. This means that it waits till the I<absolute> last possible
166moment to populate the slot. This means that if you, the user, write
167to the slot, everything happens as normal and what you pass in is stored.
168However, if you I<read> the slot, then at that I<exact> moment (and no
169sooner), the slot will be populated with the value of the C<default>
170option.
171
e08c54f5 172This option is what allows the B<BinaryTree> class to instantiate
173objects without fear of the I<infinitely recursive spiral of death>
174mentioned earlier.
8597d950 175
e08c54f5 176So, we have described a quite complex set of behaviors here, and not
177one method had to be written. But wait, we can't get away that
8597d950 178easily. The autogenerated C<right> and C<left> accessors are not
179completely correct. They will not install the parental relationships
180that we need. We could write our own accessors, but that would require
181us to implement all those features we got automatically (the type
182constraint, the lazy initialization, etc). So instead we use the
183method modifiers again.
184
185 before 'right', 'left' => sub {
186 my ($self, $tree) = @_;
187 $tree->parent($self) if defined $tree;
188 };
189
190This is a C<before> modifier, just like we saw in the second recipe,
191but with two slight differences. First, we are applying this to more
192than one method at a time. Since both the C<left> and C<right> methods
193need the same feature, it makes sense. The second difference is that
194we are not wrapping an inherited method anymore, but instead a method
195of our own local class. Wrapping local methods is no different, the
196only requirement is that the wrappee be created before the wrapper
197(after all, you cannot wrap something which doesn't exist right?).
198
199Now, as with all the other recipes, you can go about using
200B<BinaryTree> like any other Perl 5 class. A more detailed example of
db1ab48d 201usage can be found in F<t/003_recipe.t>.
8597d950 202
203=head1 CONCLUSION
204
205This recipe introduced you to some of the more advanced behavioral
206possibilities of Moose's attribute mechanism. I hope that it has
207opened your mind to the powerful possibilities of Moose. In the next
208recipe we explore how we can create custom subtypes and take
209advantage of the plethora of useful modules out on CPAN with Moose.
e08c54f5 210
8597d950 211=head1 FOOTNOTES
212
213=over 4
214
215=item (1)
216
217Weak references are tricky things, and should be used sparingly
218and appropriately (such as in the case of circular refs). If you
219are not careful, you will have slot values disappear "mysteriously"
220because perls reference counting garbage collector has gone and
221removed the item you are weak-referencing.
222
223In short, don't use them unless you know what you are doing :)
224
225=item (2)
226
227You I<can> use the C<default> option without the C<lazy> option if
228you like, as we showed in the second recipe.
229
230=back
231
471c4f09 232=head1 AUTHOR
233
234Stevan Little E<lt>stevan@iinteractive.comE<gt>
235
236=head1 COPYRIGHT AND LICENSE
237
238Copyright 2006 by Infinity Interactive, Inc.
239
240L<http://www.iinteractive.com>
241
242This library is free software; you can redistribute it and/or modify
243it under the same terms as Perl itself.
244
f7522f24 245=cut