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