Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Is Moose obliged to call builder again after call to clearer?

Tags:

perl

moose

I want to fetch elements from a list that is stored inside a Moose class. The class knows how to populate that list itself. It's sort of like an iterator, except that I want to be able to reset the iterator, and start fetching the same elements from that list as if I hadn't already done so. I intend to call as follows:

while( my $slotlist = $class->get_next_slotlist ) {
    # do something with $slotlist
}

Except that, as I said, I may want to reiterate over the same elements again at a later point:

$class->reset_slotlists;
while( my $slotlist = $class->get_next_slotlist ) {
    # do something with $slotlist
}

I thought of designing the class along the lines of the following stripped-down (mock) version:

package List;
use Moose;

has '_data' => (
    traits    => [ 'Array' ],
    is        => 'ro',
    predicate => 'has_data',
    lazy      => 1,
    builder   => '_build_data',
    clearer   => 'reset',
    handles   => {
        next => 'shift',
    },
);

sub _build_data { [ 'abc', 'def' ] }

package main;
use strict;
use warnings;
use Test::More;
use Test::Pretty;

my $list = List->new;
ok ! $list->has_data;
is $list->next, 'abc';
is $list->next, 'def';
is $list->next, undef;
is $list->next, undef;
$list->reset;
is $list->next, 'abc', 'Moose calls builder again after clearing';
is $list->next, 'def';
is $list->next, undef;
is $list->next, undef;
ok $list->has_data;

done_testing;

When I run this, Moose calls the builder again after the call to reset() (i.e., the clearer). My question is now: is this behaviour guaranteed? The documentation doesn't say.

(While typing this question, I also started wondering: do you think this is bad design? I like the iterator-like interface, very elegant on the calling side, and easy to implement. But is this very question a sign that the design isn't good?)

like image 905
Edward Avatar asked Feb 12 '14 12:02

Edward


1 Answers

Yes, if a lazy attribute has been cleared, then the next time an accessor is called, the value will be rebuilt.

Clearing _data is essentially like doing delete($self->{_data}). Or it would be if Moose objects were hashrefs, but they're not hashrefs, they're objects. (Actually they are hashrefs underneath but part of the Moose experience is that we're supposed to pretend we don't know that. Winking face.)

Lazy attributes use exists($self->{_data}) in order to decide whether the value needs to be built.

I don't think it's a bad design, but if the array is big, then keeping a copy in _data to iteratively destroy with shift is potentially wasting memory. You could just keep a counter of where you are in the array and increment it each time.

Update: You are correct that this is not documented very well.

like image 185
tobyink Avatar answered Sep 28 '22 08:09

tobyink