1Moose::Cookbook::BasicsUM:so:eoBrsienC:ao:rnCytoTrorikebbeuo_toAektd:t:rPBieabrsulitceDsFo:ec:auBtmiuenrnaetrsay(tT3ir)oene_AttributeFeatures(3)
2
3
4
6 Moose::Cookbook::Basics::BinaryTree_AttributeFeatures - Demonstrates
7 various attribute features including lazy, predicates, weak refs, and
8 more
9
11 version 2.2203
12
14 package BinaryTree;
15 use Moose;
16
17 has 'node' => ( is => 'rw', isa => 'Any' );
18
19 has 'parent' => (
20 is => 'rw',
21 isa => 'BinaryTree',
22 predicate => 'has_parent',
23 weak_ref => 1,
24 );
25
26 has 'left' => (
27 is => 'rw',
28 isa => 'BinaryTree',
29 predicate => 'has_left',
30 lazy => 1,
31 default => sub { BinaryTree->new( parent => $_[0] ) },
32 trigger => \&_set_parent_for_child
33 );
34
35 has 'right' => (
36 is => 'rw',
37 isa => 'BinaryTree',
38 predicate => 'has_right',
39 lazy => 1,
40 default => sub { BinaryTree->new( parent => $_[0] ) },
41 trigger => \&_set_parent_for_child
42 );
43
44 sub _set_parent_for_child {
45 my ( $self, $child ) = @_;
46
47 confess "You cannot insert a tree which already has a parent"
48 if $child->has_parent;
49
50 $child->parent($self);
51 }
52
54 This recipe shows how various advanced attribute features can be used
55 to create complex and powerful behaviors. In particular, we introduce a
56 number of new attribute options, including "predicate", "lazy", and
57 "trigger".
58
59 The example class is a classic binary tree. Each node in the tree is
60 itself an instance of "BinaryTree". It has a "node", which holds some
61 arbitrary value. It has "right" and "left" attributes, which refer to
62 its child trees, and a "parent".
63
64 Let's take a look at the "node" attribute:
65
66 has 'node' => ( is => 'rw', isa => 'Any' );
67
68 Moose generates a read-write accessor for this attribute. The type
69 constraint is "Any", which literally means it can contain anything.
70
71 We could have left out the "isa" option, but in this case, we are
72 including it for the benefit of other programmers, not the computer.
73
74 Next, let's move on to the "parent" attribute:
75
76 has 'parent' => (
77 is => 'rw',
78 isa => 'BinaryTree',
79 predicate => 'has_parent',
80 weak_ref => 1,
81 );
82
83 Again, we have a read-write accessor. This time, the "isa" option says
84 that this attribute must always be an instance of "BinaryTree". In the
85 second recipe, we saw that every time we create a Moose-based class, we
86 also get a corresponding class type constraint.
87
88 The "predicate" option is new. It creates a method which can be used to
89 check whether or not a given attribute has been initialized. In this
90 case, the method is named "has_parent".
91
92 This brings us to our last attribute option, "weak_ref". Since "parent"
93 is a circular reference (the tree in "parent" should already have a
94 reference to this one, in its "left" or "right" attribute), we want to
95 make sure that we weaken the reference to avoid memory leaks. If
96 "weak_ref" is true, it alters the accessor function so that the
97 reference is weakened when it is set.
98
99 Finally, we have the "left" and "right" attributes. They are
100 essentially identical except for their names, so we'll just look at
101 "left":
102
103 has 'left' => (
104 is => 'rw',
105 isa => 'BinaryTree',
106 predicate => 'has_left',
107 lazy => 1,
108 default => sub { BinaryTree->new( parent => $_[0] ) },
109 trigger => \&_set_parent_for_child
110 );
111
112 There are three new options here, "lazy", "default", and "trigger". The
113 "lazy" and "default" options are linked. In fact, you cannot have a
114 "lazy" attribute unless it has a "default" (or a "builder", but we'll
115 cover that later). If you try to make an attribute lazy without a
116 default, class creation will fail with an exception. (2)
117
118 In the second recipe the BankAccount's "balance" attribute had a
119 default value of 0. Given a non-reference, Perl copies the value.
120 However, given a reference, it does not do a deep clone, instead simply
121 copying the reference. If you just specified a simple reference for a
122 default, Perl would create it once and it would be shared by all
123 objects with that attribute.
124
125 As a workaround, we use an anonymous subroutine to generate a new
126 reference every time the default is called.
127
128 has 'foo' => ( is => 'rw', default => sub { [] } );
129
130 In fact, using a non-subroutine reference as a default is illegal in
131 Moose.
132
133 # will fail
134 has 'foo' => ( is => 'rw', default => [] );
135
136 This will blow up, so don't do it.
137
138 You'll notice that we use $_[0] in our default sub. When the default
139 subroutine is executed, it is called as a method on the object.
140
141 In our case, we're making a new "BinaryTree" object in our default,
142 with the current tree as the parent.
143
144 Normally, when an object is instantiated, any defaults are evaluated
145 immediately. With our "BinaryTree" class, this would be a big problem!
146 We'd create the first object, which would immediately try to populate
147 its "left" and "right" attributes, which would create a new
148 "BinaryTree", which would populate its "left" and "right" slots.
149 Kaboom!
150
151 By making our "left" and "right" attributes "lazy", we avoid this
152 problem. If the attribute has a value when it is read, the default is
153 never executed at all.
154
155 We still have one last bit of behavior to add. The autogenerated
156 "right" and "left" accessors are not quite correct. When one of these
157 is set, we want to make sure that we update the parent of the "left" or
158 "right" attribute's tree.
159
160 We could write our own accessors, but then why use Moose at all?
161 Instead, we use a "trigger". A "trigger" accepts a subroutine
162 reference, which will be called as a method whenever the attribute is
163 set. This can happen both during object construction or later by
164 passing a new object to the attribute's accessor method. However, it is
165 not called when a value is provided by a "default" or "builder".
166
167 sub _set_parent_for_child {
168 my ( $self, $child ) = @_;
169
170 confess "You cannot insert a tree which already has a parent"
171 if $child->has_parent;
172
173 $child->parent($self);
174 }
175
176 This trigger does two things. First, it ensures that the new child node
177 does not already have a parent. This is done for the sake of
178 simplifying the example. If we wanted to be more clever, we would
179 remove the child from its old parent tree and add it to the new one.
180
181 If the child has no parent, we will add it to the current tree, and we
182 ensure that is has the correct value for its "parent" attribute.
183
184 As with all the other recipes, BinaryTree can be used just like any
185 other Perl 5 class. A more detailed example of its usage can be found
186 in t/recipes/basics_binarytree_attributefeatures.t.
187
189 This recipe introduced several of Moose's advanced features. We hope
190 that this inspires you to think of other ways these features can be
191 used to simplify your code.
192
194 (1) Weak references are tricky things, and should be used sparingly and
195 appropriately (such as in the case of circular refs). If you are
196 not careful, attribute values could disappear "mysteriously"
197 because Perl's reference counting garbage collector has gone and
198 removed the item you are weak-referencing.
199
200 In short, don't use them unless you know what you are doing :)
201
202 (2) You can use the "default" option without the "lazy" option if you
203 like, as we showed in the second recipe.
204
205 Also, you can use "builder" instead of "default". See
206 Moose::Cookbook::Basics::BinaryTree_BuilderAndLazyBuild for
207 details.
208
210 • Stevan Little <stevan@cpan.org>
211
212 • Dave Rolsky <autarch@urth.org>
213
214 • Jesse Luehrs <doy@cpan.org>
215
216 • Shawn M Moore <sartak@cpan.org>
217
218 • יובל קוג'מן (Yuval Kogman) <nothingmuch@woobling.org>
219
220 • Karen Etheridge <ether@cpan.org>
221
222 • Florian Ragwitz <rafl@debian.org>
223
224 • Hans Dieter Pearcey <hdp@cpan.org>
225
226 • Chris Prather <chris@prather.org>
227
228 • Matt S Trout <mstrout@cpan.org>
229
231 This software is copyright (c) 2006 by Infinity Interactive, Inc.
232
233 This is free software; you can redistribute it and/or modify it under
234 the same terms as the Perl 5 programming language system itself.
235
236
237
238perl v5.36.0 Moose::Cookb2o0o2k3:-:0B2a-s0i6cs::BinaryTree_AttributeFeatures(3)