use warnings;
use Moose;
+ has 'node' => (is => 'rw', isa => 'Any');
+
has 'parent' => (
- is => 'rw',
- isa => 'BinaryTree',
+ is => 'rw',
+ isa => 'BinaryTree',
predicate => 'has_parent',
- weak_ref => 1,
+ weak_ref => 1,
);
has 'left' => (
- is => 'rw',
- isa => 'BinaryTree',
+ is => 'rw',
+ isa => 'BinaryTree',
predicate => 'has_left',
lazy => 1,
default => sub { BinaryTree->new(parent => $_[0]) },
);
has 'right' => (
- is => 'rw',
- isa => 'BinaryTree',
+ is => 'rw',
+ isa => 'BinaryTree',
predicate => 'has_right',
lazy => 1,
default => sub { BinaryTree->new(parent => $_[0]) },
=head1 DESCRIPTION
+In this recipe we take a closer look at attributes, and see how
+some of their more advanced features can be used to create fairly
+complex behaviors.
+
+The class in this recipe is a classic binary tree, each node in the
+tree is represented by an instance of the B<BinaryTree> class. Each
+instance has a C<node> slot to hold an abitrary value, a C<right>
+slot to hold the right node, a C<left> slot to hold the left node,
+and finally a C<parent> slot to hold a reference back up the tree.
+
+Now, lets start with the code, our first attribute is the C<node>
+slot, defined as such:
+
+ has 'node' => (is => 'rw', isa => 'Any');
+
+If you recall from the previous recipies, this slow will have a
+read/write accessor generated for it, and has a type constraint on it.
+The new item here is the type constraint of C<Any>. In the type
+constraint heirarchy in L<Moose::Utils::TypeConstraints>, the C<Any>
+constraint is the "root" of the hierarchy. It means exactly what it
+says, it allows anything to pass. Now, you could just as easily of left
+the C<isa> out, and left the C<node> slot unconstrainted and gotten the
+same behavior. But here, we are really including the type costraint
+for the benefit of other programmers, not the computer. It makes
+clear my intent that the C<node> can be of any type, and that the
+class is a polymorphic container. Next, lets move onto the C<parent>
+slot.
+
+ has 'parent' => (
+ is => 'rw',
+ isa => 'BinaryTree',
+ predicate => 'has_parent',
+ weak_ref => 1,
+ );
+
+As you already know from reading the previous recipes, this code
+tells you that C<parent> gets a read/write accessor, is constrainted
+to only accept instances of B<BinaryTree>. You will of course remember
+from the second recipe that the C<BinaryTree> type constraint is
+automatically created for us by Moose.
+
+The next attribute option is new though, the C<predicate> option.
+This option creates a method, which can be used to check to see if
+a given slot (in this case C<parent>) has a defined value in it. In
+this case it will create a method called C<has_parent>. Quite simple,
+and also quite handy too.
+
+This brings us to our last attribute, and also a new one. Since the
+C<parent> is a circular reference (the tree in C<parent> should
+already have a reference in either it's C<left> or C<right> nodes),
+we want to make sure that it is also a weakened reference to avoid
+memory leaks. The C<weak_ref> attribute option will do just that,
+C<weak_ref> simply takes a boolean value (C<1> or C<0>) and it will
+then add the extra capability to the accessor function to weaken
+the reference of any value stored in the C<parent> slot (1).
+
+Now, onto the C<left> and C<right> attributes. They are essentially
+the same things, only with different names, so I will just describe
+one here.
+
+ has 'left' => (
+ is => 'rw',
+ isa => 'BinaryTree',
+ predicate => 'has_left',
+ lazy => 1,
+ default => sub { BinaryTree->new(parent => $_[0]) },
+ );
+
+You already know what the C<is>, C<isa> and C<>predicate> options
+do, but now we have two more new options. These two options are
+actually linked together, in fact, you cannot use the C<lazy>
+option unless you have set the C<default> option. The class
+creation will fail with an exception (2).
+
+Before I go into detail about how C<lazy> works, let me first
+explain how C<default> works, and in particular why it is wrapped
+in a CODE ref.
+
+In the second recipe the B<BankAccount>'s C<balance> slot had a
+default value of C<0>. Since Perl will copy strings and numbers
+by value, this was all we had to say. But for any other item
+(ARRAY ref, HASH ref, object instance, etc) Perl will copy by
+reference. This means that if I were to do this:
+
+ has 'foo' => (is => 'rw', default => []);
+
+Every single instance of that class would get a pointer to the
+same ARRAY ref in their C<foo> slot. This is almost certainly
+B<not> the behavior you intended. So, the solution is to wrap
+these defaults into an anon-sub, like so:
+
+ has 'foo' => (is => 'rw', default => sub { [] });
+
+This assures that each instance of this class will get it's own
+ARRAY ref in the C<foo> slot.
+
+One other feature of the sub ref version of the C<default> option
+is that when the subroutine is executed (to get back the expected
+default value), we also pass in the instance where the slot will
+be stored. This added feature can come in quite handy at times, as
+is illustrated above, with this code:
+
+ default => sub { BinaryTree->new(parent => $_[0]) },
+
+The default value being generated is a new C<BinaryTree> instance
+for the C<left> (or C<right>) slot. Here we set up the parental
+relationship by passing the current instance to the constructor.
+
+Now, before we go on to the C<lazy> option, I want you to think
+for a moment. When an instance of this class is created, and the
+slots are being initialized, the "normal" behavior would be for
+the C<left> and C<right> slots to be populated with a new instance
+of B<BinaryTree>. In creating that instance of the C<left> or
+C<right> slots, we would need to create new instances to populate
+the C<left> and C<right> slots of I<those> instances. This would
+continue in an I<infinitely recursive spiral of death> until you had
+exhausted all available memory on your machine.
+
+This is, of course, not good :)
+
+Which brings us to the C<lazy> attribute option. The C<lazy> option
+does just what it says. It lazily initializes the slot within the
+instance. This means that it waits till the I<absolute> last possible
+moment to populate the slot. This means that if you, the user, write
+to the slot, everything happens as normal and what you pass in is stored.
+However, if you I<read> the slot, then at that I<exact> moment (and no
+sooner), the slot will be populated with the value of the C<default>
+option.
+
+This option is what allows the B<BinaryTree> class to instantiate
+objects without fear of the I<infinitely recursive spiral of death>
+I mentioned earlier.
+
+So, we have descibed a quite complex set of behaviors here, and not
+one method has needed to be written. But wait, we can't get away that
+easily. The autogenerated C<right> and C<left> accessors are not
+completely correct. They will not install the parental relationships
+that we need. We could write our own accessors, but that would require
+us to implement all those features we got automatically (the type
+constraint, the lazy initialization, etc). So instead we use the
+method modifiers again.
+
+ before 'right', 'left' => sub {
+ my ($self, $tree) = @_;
+ $tree->parent($self) if defined $tree;
+ };
+
+This is a C<before> modifier, just like we saw in the second recipe,
+but with two slight differences. First, we are applying this to more
+than one method at a time. Since both the C<left> and C<right> methods
+need the same feature, it makes sense. The second difference is that
+we are not wrapping an inherited method anymore, but instead a method
+of our own local class. Wrapping local methods is no different, the
+only requirement is that the wrappee be created before the wrapper
+(after all, you cannot wrap something which doesn't exist right?).
+
+Now, as with all the other recipes, you can go about using
+B<BinaryTree> like any other Perl 5 class. A more detailed example of
+usage can be found in F<t/003_basic.t>.
+
+=head1 CONCLUSION
+
+This recipe introduced you to some of the more advanced behavioral
+possibilities of Moose's attribute mechanism. I hope that it has
+opened your mind to the powerful possibilities of Moose. In the next
+recipe we explore how we can create custom subtypes and take
+advantage of the plethora of useful modules out on CPAN with Moose.
+
+=head1 FOOTNOTES
+
+=over 4
+
+=item (1)
+
+Weak references are tricky things, and should be used sparingly
+and appropriately (such as in the case of circular refs). If you
+are not careful, you will have slot values disappear "mysteriously"
+because perls reference counting garbage collector has gone and
+removed the item you are weak-referencing.
+
+In short, don't use them unless you know what you are doing :)
+
+=item (2)
+
+You I<can> use the C<default> option without the C<lazy> option if
+you like, as we showed in the second recipe.
+
+=back
+
=head1 AUTHOR
Stevan Little E<lt>stevan@iinteractive.comE<gt>