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

In OO Perl, it's a good idea to avoid "multiple inheritance" which is when an object belongs to two or more classes. To avoid this problem, Damian Conway offers this bit of code to be placed in the initialization subroutines within a special initialization class (p. 175 of OO Perl) to avoid the problem:
sub _init { my ($self, %args) = @_;
   return if $self->{_init}{__PACKAGE__}++;
... }
</code> I think I'm overloaded with too much Perl because I am completely missing how this simple code works. Apparently, the above syntax creates an attribute for the object, causing a return out of the current subroutine if the attribute is non-zero. But how does throwing a class, {__PACKAGE__}, on the right side of an object method call, $self->{_init}, create an attribute?

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

Replies are listed 'Best First'.
(Ovid) Re: Syntax for preventing repeat object initializations
by Ovid (Cardinal) on Jul 17, 2001 at 04:02 UTC

    $self is a hashref and __PACKAGE__ is the package in the class that it's in. If this code is in package "Foo", then the '_init' key in self is auto-incremented. The first time this occurs, it will evaluate as false, allowing Foo::_init to function, but subsequent calls will fail.

    nysus wrote:

    But how does throwing a class, {__PACKAGE__}, on the right side of an object method call, $self->{_init}, create an attribute?

    You're simply auto-vivifying a hash key with the name being equal to the current package name. You see, Perl has an interesting shortcut for complex data structures. In this case, the following are equivalent:

    $self->{_init}->{__PACKAGE__}; $self->{_init}{__PACKAGE__};
    If you've dereferenced the first reference, you're allowed to drop the arrow notation for subsequent dereferences. Thus, $self->{_init} becomes a hashref on the fly by merely appending curly braces.

    Cheers,
    Ovid

    Vote for paco!

    Join the Perlmonks Setiathome Group or just click on the the link and check out our stats.

Re: Syntax for preventing repeat object initializations
by japhy (Canon) on Jul 17, 2001 at 04:09 UTC
    Funnily enough, {__PACKAGE__} could just as well be {__gobbledegook__}. The __PACKAGE__ compile-time token doesn't get substituted with its actual value if it appears to the left of => or inside braces.

    _____________________________________________________
    Jeff japhy Pinyan: Perl, regex, and perl hacker.
    s++=END;++y(;-P)}y js++=;shajsj<++y(p-q)}?print:??;

      Very interesting. I created the following package:

      package Scrap::Foo; use strict; sub bar { my $dummy; $dummy->{_init}->{__PACKAGE__}++; return $dummy; } 1

      And my test code:

      use strict; use Scrap::Foo; use Data::Dumper; my $dummy = Scrap::Foo::bar(); print Dumper $dummy;

      Generated this output:

      $VAR1 = { '_init' => { '__PACKAGE__' => '1' } };

      And the fix:

      my $package = __PACKAGE__; $dummy->{_init}->{$package}++;

      Thanks for the info. Now who wants to tell Damian? :)

      Cheers,
      Ovid

      Update: japhy's unary fix is not only shorter, but it should be faster as it doesn't involve creating a variable for no other reason than throwing it away.

      Vote for paco!

      Join the Perlmonks Setiathome Group or just click on the the link and check out our stats.

        The "unary +" fix is shorter...
        $foo->{+__PACKAGE__} = 1;
        Hmm, patching a book isn't quite so easy!

        _____________________________________________________
        Jeff japhy Pinyan: Perl, regex, and perl hacker.
        s++=END;++y(;-P)}y js++=;shajsj<++y(p-q)}?print:??;

Re: Syntax for preventing repeat object initializations
by chromatic (Archbishop) on Jul 17, 2001 at 04:05 UTC
    That's not a method call, it's accessing a hash member through a reference. $self contains a member called '_init' which holds another hash reference.

    That code simply increments a marker in that sub-hash. The key is the name of the current package (respecting inheritance). If _init() has already been called for the package, the key will already have been autovivified, and the value of the expression will be true.

    Make sense?

Re: Syntax for preventing repeat object initializations
by toma (Vicar) on Jul 17, 2001 at 06:35 UTC
    There is extensive errata to Damian's OO book at the Manning website. I spent a while marking up my copy. This minor problem might be worth adding to the errata, so I think you should go ahead and report this one to Damian, nysus. Have no fear, he's an easy-going fellow.

    It should work perfectly the first time! - toma

      This one is not yet listed on the Manning site so I submitted it (before I saw this note).
(bbfu) Re: Syntax for preventing repeat object initializations
by bbfu (Curate) on Jul 17, 2001 at 04:37 UTC

    Hrm. Are you sure, nysus, that this code helps prevent multiple inheritance? As I see it, the __PACKAGE__ key (barring the minor bug) would only prevent the object from being re-initialized in the same package/class, not by other classes. (And that's even assuming all other classes that might bless the object follow this convention!)

    Please enlighten me if I am missing something, though! :-)

    bbfu
    Seasons don't fear The Reaper.
    Nor do the wind, the sun, and the rain.
    We can be like they are.

      No, I'm not sure. Don't pay much heed to what I write. I'm a total OO newbie and I've been cramming Conway's book down my throat for the past few days. My ability to retain all the info in it is beginning to wane. It's time for me to practice with my own simple code and then come back to the book when my knowledge is more thorough.

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