nysus has asked for the wisdom of the Perl Monks concerning the following question:

I've got one large object. I have dozens of other, smaller objects. Within each of these smaller objects, I'd like to store the one large object in an attribute so the smaller objects can refer to it easily. Is there an advantage to storing the large object as a reference to the large object instead of the object itself?

I think it won't make a difference because the large object is itself essentially a reference to the data. But I just want to make sure. Or maybe there is some other reason I should store a reference to the larger object that I'm not thinking of? Thanks.

Follow up question: I'm using Moose. Should I consider breaking out the MooseX::ClassAttribute module to store the large object? How, exactly, is using a class object any different than storing the large object in all the objects? Does it buy me anything?

$PM = "Perl Monk's";
$MCF = "Most Clueless Friar Abbot Bishop Pontiff Deacon Curate Priest";
$nysus = $PM . ' ' . $MCF;
Click here if you love Perl Monks

  • Comment on Is there an advantage to storing references to objects in an attribute instead of the object itself?

Replies are listed 'Best First'.
Re: Is there an advantage to storing references to objects in an attribute instead of the object itself?
by haukex (Archbishop) on Dec 02, 2017 at 11:02 UTC
    Is there an advantage to storing the large object as a reference to the large object instead of the object itself?

    An object in Perl is just an anonymous data structure (it just happens to be blessed) that is referred to by one or more references, so in code that uses the object you'd never really store "the object itself" in a variable, but only a reference. You are free to take as many references to the same object as you like*, but in regards to "storing the object itself", I don't understand the question, and I think a Short, Self-Contained, Correct Example would help.

    * However, if you have your "one big object" storing references to your "smaller objects", and your smaller objects referring back to the big one, you've created circular references that will prevent the objects from being garbage-collected, in other words, your program has a memory leak. One way to deal with this is to weaken one of the references on one end with weaken from Scalar::Util. Note that even code like you showed in the reply here will still mean there are circular references.

    In the following code, try commenting out the line weaken($self->{big_obj}); and see how the output changes to show the memory leak.

    use warnings; use strict; { package Big; sub new { my $class = shift; bless {@_}, $class } sub new_little { my ($self,$id) = @_; push @{$self->{little_objs}}, Little->new(id=>$id, big_obj=>$self); return $self; } sub DESTROY { print "DESTROY ",__PACKAGE__," id=",shift->{id},"\n" } } { package Little; use Scalar::Util qw/weaken/; sub new { my $class = shift; my $self = {@_}; weaken($self->{big_obj}); return bless $self, $class; } sub DESTROY { print "DESTROY ",__PACKAGE__," id=",shift->{id},"\n" } } my $big1 = Big->new(id=>"One")->new_little("Hello"); print "Clearing \$big1...\n"; $big1 = undef; # no more references to object should cause GC my $big2 = Big->new(id=>"Two")->new_little("World"); print "Clearing \$big2...\n"; $big2 = undef; END { print "END\n" }

    Minor edits for clarity.

      OK, I ran your code. Very interesting. Thanks for illustrating this problem for me so clearly. Circular references are one of those concepts I blew off and didn't worry about. It's probably time I started worrying.

      So what's the best solution to my problem? Here's my self-contained code that illustrates what I'm doing:

      #! /usr/bin/env perl use warnings; use strict; package BigObjs; use Moose; has 'little_objs' => ( is => 'rw', isa => 'HashRef', default => sub { +{} } ); # our reader method sub get_little_obj { my ($s, $id) = @_; return $s->little_objs->{$id}; } # add new little objects to our collection sub add_little_obj { my ($s, $little_obj) = @_; # create code ref to reader method in big object $little_obj->get_obj_by_id(sub{$s->get_little_obj(@_)}); $s->little_objs->{$little_obj->id} = $little_obj; } package LittleObjs; use Moose; has 'id' => (is => 'rw'); has 'data' => (is => 'rw'); has 'get_obj_by_id' => (is => 'rw'); sub alter_another_little_object { my ($s, $id) = @_; my $other_little_object = $s->get_obj_by_id->($id); $other_little_object->do_stuff; } sub do_stuff { my $s = shift; print "I'm doing stuff to: " . $s->id . "\n"; } no Moose; my $big = BigObjs->new(); my $little1 = LittleObjs->new(id => 'little1', data => 'foo'); my $little2 = LittleObjs->new(id => 'little2', data => 'bar'); $big->add_little_obj($little1); $big->add_little_obj($little2); $little1->alter_another_little_object('little2');

      So you are saying I need to use sub{$s->get_little_obj(@_)} as an argument to a weaken function?

      $PM = "Perl Monk's";
      $MCF = "Most Clueless Friar Abbot Bishop Pontiff Deacon Curate Priest";
      $nysus = $PM . ' ' . $MCF;
      Click here if you love Perl Monks

        To reply to your various posts in this thread in one place:

        Circular references are one of those concepts I blew off and didn't worry about. It's probably time I started worrying.

        It's definitely something to keep in mind when building object trees like you are. I tried out some different ways to locate circular references in this node, but applying that to your code just shows a bunch of circular references of Class::MOP::* objects, so I'd guess Moose is wrapping everything up and hiding the actual circular references. Applying it to my code above shows them a little more clearly.

        I stored a reference to a reader method ... it provides encapsulation

        Personally, I don't think hiding the "big object" in a code ref adds much. Consider that the class for the big object should already provide all the necessary encapsulation, and so I think there's nothing wrong with simply providing a getter on the "little object" class that returns the big object. You might just consider making this field read-only.

        ... the weak_ref => 1 attribute property. However, slapping that in breaks my code.

        Yes, I can confirm that in your code putting weak_ref on either the little_objs or get_obj_by_id causes "Can't use an undefined value as a HASH/subroutine reference" errors. Also, if I take your code as shown here, and add a sub DESTROY to track destruction like I showed above, it shows the memory leak. Since adding a level of indirection with the code ref doesn't seem to break my code above, at the moment I'm guessing that it's the combination of Moose's weak_ref and the code ref that is causing the problem, but I haven't investigated this further, because I can fix the problem by doing what I said above, just giving the little object a reference to the big object:

        I'd probably just store a weak reference to the big object directly in the little one, and use it in alter_another_little_object to get the other object:
        #! /usr/bin/perl use warnings; use strict; { package BigObjs; use Moose; has 'little_objs' => (is => 'rw', isa => 'HashRef', default => sub + { {} }); sub get_little_obj { my ($s, $id) = @_; return $s->little_objs->{$id}; } sub add_little_obj { my ($s, $little_obj) = @_; $little_obj->big_obj($s); $s->little_objs->{$little_obj->id} = $little_obj; } } { package LittleObjs; use Moose; has id => (is => 'rw'); has data => (is => 'rw'); has big_obj => (is => 'rw', weak_ref => 1); sub alter_another_little_object { my ($s, $id) = @_; my $other_little_object = $s->big_obj->get_little_obj($id); $other_little_object->do_stuff; } sub do_stuff { my $s = shift; print "I'm doing stuff to: " . $s->id . "\n"; } } my $big = BigObjs->new(); my $little1 = LittleObjs->new(id => 'little1', data => 'foo'); my $little2 = LittleObjs->new(id => 'little2', data => 'bar'); $big->add_little_obj($little1); $big->add_little_obj($little2); $little1->alter_another_little_object('little2'); warn "Done\n";
        ($q=q:Sq=~/;[c](.)(.)/;chr(-||-|5+lengthSq)`"S|oS2"`map{chr |+ord }map{substrSq`S_+|`|}3E|-|`7**2-3:)=~y+S|`+$1,++print+eval$q,q,a,

        OK, so everything works if I put in the full object into the attribute with weak_ref in the attribute. Thanks so much! I greatly appreciate your consideration. Onward!

        $PM = "Perl Monk's";
        $MCF = "Most Clueless Friar Abbot Bishop Pontiff Deacon Curate Priest";
        $nysus = $PM . ' ' . $MCF;
        Click here if you love Perl Monks

        OK, I see in Moose I have to use the weak_ref => 1 attribute property. However, slapping that in breaks my code. Hmm.

        $PM = "Perl Monk's";
        $MCF = "Most Clueless Friar Abbot Bishop Pontiff Deacon Curate Priest";
        $nysus = $PM . ' ' . $MCF;
        Click here if you love Perl Monks

Re: Is there an advantage to storing references to objects in an attribute instead of the object itself?
by dsheroh (Monsignor) on Dec 02, 2017 at 08:48 UTC
    Erm... Objects are references in Perl. If you're distinguishing between an object and a reference to an object, than that's the difference between a reference to (usually) a hash and a reference to a reference to a hash. Maybe there's something weird going on in your use case that you haven't mentioned, but, in general, there's no benefit to storing a reference to a reference instead of the original reference itself.
Re: Is there an advantage to storing references to objects in an attribute instead of the object itself?
by nysus (Parson) on Dec 02, 2017 at 06:28 UTC

    So I did something different. Instead of storing the large object in an attribute, I stored a reference to a reader method I created in the large object:

    use Moose; my $little_obj = Class->new( { get_big_obj_data => sub{ $big_object->g +et_data(@_) } } ); # retrieve data like this: my $data = $little_obj->get_big_obj_data->(@args);

    Seems like this is a much smarter thing to do because it provides encapsulation.

    $PM = "Perl Monk's";
    $MCF = "Most Clueless Friar Abbot Bishop Pontiff Deacon Curate Priest";
    $nysus = $PM . ' ' . $MCF;
    Click here if you love Perl Monks

Re: Is there an advantage to storing references to objects in an attribute instead of the object itself?
by Anonymous Monk on Dec 02, 2017 at 08:04 UTC
    So you have a god object? Nah, not it ... What do you got? Class attributes always struck me as static-y things.. :/
A reply falls below the community's threshold of quality. You may see it by logging in.