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