in reply to Why not use a local variable for $self in Moose?

The problem will come as soon as you have more than one instance of your object. They will both overwrite the $self in global scope.

If you only have one object, a Singleton, then it makes (some) sense to do that, but I would use MooseX::Singleton to implement that directly.

Replies are listed 'Best First'.
Re^2: Why not use a local variable for $self in Moose?
by nysus (Parson) on Oct 19, 2018 at 07:34 UTC

    By the way, I'm glad I asked such a dumb question. Your mention of "singleton" finally shed some light for me on what a singleton might be used for. I never used them and wasn't quite sure why they might be used.

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

      I've got an example of where I use a singleton. It's a Dancer2 web application with a JS/jQuery front end. It has a separate module for the API, and one for DB.

      All users who view the page see the exact same results. When the page is updated on one device, it's auto-updated on all others as well.

      Both the API module (class) and DB module (class) are singletons (as is the log object). This ensures that each user loading the page are using the exact same object, so all configuration of the objects are consistent on all runs. If the DB or API objects modify themselves throughout the course of their duties, all instances of the application immediately have those attributes/changes provided.

      Here's some of the code from the API module:

      # the $api object is declared up here in file scope sub new { # return the stored object if we've already run new() if (defined $api){ $log->_5('returning stored API object'); return $api; } my $self = bless {}, shift; my $caller = (caller)[0]; $self->_args(@_, caller => $caller); warn "API in test mode\n" if $self->testing; $self->_init; $api = $self; $log->_5("successfully initialized the system"); if (! $self->testing && ! defined $events){ $self->events; $log->_5('successfully created new async events') } else { $log->_5("async events have already been spawned"); } return $self; }
Re^2: Why not use a local variable for $self in Moose?
by nysus (Parson) on Oct 19, 2018 at 06:44 UTC

    Ah, OK. So I understand what you are saying but I'm not quite sure why it is so. I don't have a clear idea of how memory gets allocated for a class. How does $self get assigned to the same exact memory location every time a new object is created? I guess this happens when the compiler sets up a "prototype" (for lack of a better word) for the object class?

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

      How does $self get assigned to the same exact memory location every time a new object is created?

      In normal usage, you don't get the same exact memory location every time a new object is created. Quite on the contrary: Every object has its own value for $self which is returned by the new() method as a reference to a new (!) hash. You might get the same memory location if you drop an object and then create another one, if Perl's memory management decides that way.

      In your code example, $self is a lexical variable which gets a location in memory once, when the module's code is evaluated. The value of $self gets overwritten every time a new object is created, so the return value of kool_func_for_users changes with every new object - but then, it does so for all objects on that class. In effect, you have created a "class attribute" which has the name $self but in your case should actually be called $most_recent_object. There are valid use cases for these, and there's also a Moose module MooseX::ClassAttribute.

      Here's a short demo (in which I've also fixed your attribute syntax by using parens instead of braces, and by using is and isa as intended by Moose):

      use strict; use warnings; use 5.010; # saving some vertical space package My::Demo; use Moose; has 'why_pass_self' => ( is => 'ro', isa => 'Str', default => 'dunno, is this ok?' ); my $self; sub BUILD { my $s = shift; $self = $s; } sub _kool_func { say $self->why_pass_self; } sub kool_func_for_users { my $self = shift; $self->_kool_func; } ##################################################################### package main; use Scalar::Util qw(refaddr); my $o1 = My::Demo->new; print "o1 ", refaddr($o1), " at says: "; $o1->kool_func_for_users; my $o2 = My::Demo->new(why_pass_self => "That's why!"); print "o2 ", refaddr($o2), " at says: "; $o2->kool_func_for_users; print "o1 ", refaddr($o1), " at says: "; $o1->kool_func_for_users;
      PS: I just noticed I've provided what Corion suggested:
      Maybe consider just creating two objects of the same such class and watch what happens.
      I don't have a clear idea of how memory gets allocated for a class.

      Remember that in Perl, An Object is Simply a Data Structure. Even though Moose may add a ton of functionality, in the end, a Moose object is just a blessed hashref. When you allocate a new anonymous hash with {}, that's newly allocated memory. However, variables defined at the package level belong to that package - they are set up when the package is parsed and executed, which typically happens only once; a new package variable isn't instantiated because you created an instance of a class.

      No, your $self in global scope is just one variable that then gets overwritten every time you instantiate a new object. The old one automatically gets DESTROYed at that time.

      Maybe consider just creating two objects of the same such class and watch what happens.

        OK, I think I get it. There can only be one $my_package::self variable in the compiled code at a time.

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