views:

148

answers:

5
package JustTesting;
use strict;
use warnings;

sub new {
    my $self = {};
    bless($self, shift);
    END { $self->goodbye() };
    return $self;
}

sub goodbye {
    print "Goodbye.\n";
}

package main;
my $this = JustTesting->new();

Output:

Variable "$self" will not stay shared at ./test line 10.
Goodbye.

Apparently it works and I can suppress the warning with no warnings inside the END block. But I wonder if there is a better way how to do this.

I tried using an anonymous sub like this:

my $cleanup = sub { $self->goodbye() };
END { $cleanup->() };

and then like this:

END { sub { $self->goodbye() }->() };

But I always get the same warning.

+17  A: 

You most likely want DESTROY instead of END. See also the section on destructors in perltoot.

package JustTesting;
use strict;
use warnings;

sub new {
    my $self = {};
    bless($self, shift);
    return $self;
}

sub goodbye {
    print "Goodbye.\n";
}

sub DESTROY {
    my ($self) = @_;
    $self->goodbye()
};


package main;
{
    say "entering scope";
    my $this = JustTesting->new();
    say "leaving scope";
}
say "left scope";

Output:

entering scope
leaving scope
Goodbye.
left scope
daxim
Yup, that's what I was looking for. Thanks! Now I should probably go off and buy a book about object-oriented design methodology, and bang my forehead with it for the next six months or so.
wuwuwu
+4  A: 

First, see my() Scoped Variable in Nested Subroutines for an explanation.

Second, I think you should either use DESTROY or a helper class depending on what you are trying to achieve.

Sinan Ünür
+1  A: 

By the time the END block is called, $cleanup has already been deallocated. To do any work with a variable, you should use a destructor instead.

See "destructors" at perldoc perltoot.

Ether
+1  A: 

The reason for the warning is because even though it doesn't look like it, to perl an END block is equivalent to the declaration of a regular (named) sub, and the behavior of a named sub declared inside of another sub isn't what you want -- $self inside the END block will be bound to $self in sub new the first time that new is called, and it will continue to see that same value -- the very first instance created -- for the rest of the life of your program. The first instance will have a reference held by the END block and won't be destroyed until the end of the program, and any later instances won't have that END block called on them at all.

Unlike named subs, anonymous subs don't have this problem because they're re-constructed every time their definition is encountered, so any variables they close over are bound as late as possible, and the values you actually want are captured.

This is a pretty long explanation when others have already told you that what you want is DESTROY instead, but I thought you might like to know what's actually happening with the code you wrote.

hobbs
+2  A: 

Just for reference to future readers I've attached a Moose version of daxim's correct answer.

use 5.012;
use warnings;

{
    package JustTesting;
    use Moose;
    use namespace::clean -except => 'meta';

    sub goodbye { say "Goodbye." }

    sub DEMOLISH {
        my ($self) = @_;
        $self->goodbye;
    }
}

{
    say "entering scope";
    my $this = JustTesting->new();
    say "leaving scope"; 
}

say "left scope";

Note the use of the DEMOLISH subroutine for the destructor.

NB. You will find that DESTROY still works but DEMOLISH is the correct Moosey way of doing it.

/I3az/

draegtun