?

Log in

No account? Create an account

Fri, Nov. 16th, 2007, 12:09 pm
You Get Me Closer To God

And ooooooooooooh, the music puns get worse and worse.

So, at work we stumbled across an interesting Perlism. Well, interesting if you're a huge nerd which, must as it pains me, I'm going to have to concede.

Anyway, so Perl has the concept of a DESTROY method which gets called on an object when it's, err, destroyed. Also, in Perl, objects can be any kind of a blessed scalar value - usually it's a hash reference but it could be a blessed array, or a bless sub routine reference. Which is where we join the story. Observe this:
my $foo = Foo->new;
$foo    = undef;

package Foo;

sub new {
    my $class = shift;
    return bless sub { print "Hello from Foo\n" }, $class; 
}
sub DESTROY {
    my $self = shift;
    $self->();
    print "Destroying Foo\n";
}
1;

We'd expect it to print
Hello from Foo
Destroying Foo

But it prints nothing.

How weird.

However if we do this (note the $class in the print statement):
my $foo = Foo->new;
$foo    = undef;

package Foo;

sub new {
    my $class = shift;
    return bless sub { print "Hello from $class\n" }, $class; 
}
sub DESTROY {
    my $self = shift;
    $self->();
    print "Destroying Foo\n";
}
1;

We get both statements printed.

Freaky, ne c'est pas?

So the reason, as far as I know is that DESTROY is only called when all references to the object are destroyed. Presumably for optimisation reasons, the anonymous subroutine in the first case is invariant and the new subroutine takes a reference to it and so the DESTROY is not called until the new goes away which it won't because the symbol table isn't wiped.

So the reason why the second one works is that by referencing the $class variable the anonymous sub is upgraded to a closure and a closure gets a new copy for every instantiation and thus gets DESTROYEDed.

It all makes sense if you squint at it askance but it's still the sort of thing that will trip you up.

Fri, Nov. 16th, 2007 01:57 pm (UTC)
2shortplanks

Aren't the two examples identical?

Fri, Nov. 16th, 2007 02:16 pm (UTC)
easterbunny

I had to do a triple take to figure out the difference. The first example says

return bless sub { print "Hello from Foo\n" }, $class;

and the second says

return bless sub { print "Hello from $class\n" }, $class;

"Hello from Foo" versus "Hello from $class". I spent 30 minutes last week arguing with someone about some bandpass code. I smugly pointed out that throwing out all data less than or equal to 847 nm as well as all data greater than or equal to 847 nm was not going to leave much to work with.

The other guy pointed out that the second number was, in fact, 874 nm.

Fri, Nov. 16th, 2007 02:38 pm (UTC)
deflatermouse

Wot Ms Bunny sez.

I shall update and make it more clear, it is somewhat subtle.

Fri, Nov. 16th, 2007 05:47 pm (UTC)
ivorjawa

Whenever I look at perl objects, it makes me think of INTERCAL.

Nothing this bad could have come about accidentally.

Fri, Nov. 16th, 2007 06:10 pm (UTC)
crucially

You are correct.

If you capture a variable in the closure, it gets copied into it's own. Otherwise it just keeps a reference to the original one. Since you aren't doing anything with class in the first one, it doesn't really matter, except in this case.

Even when it clones, it uses the same op tree, just a different data space. Devel::Peek will tell you.

Sat, Nov. 17th, 2007 09:00 am (UTC)
deflatermouse

Nice to have it confirmed :)

Sat, Nov. 17th, 2007 02:02 am (UTC)
dan_erat

s/Bring/Get/, no?

Sat, Nov. 17th, 2007 08:59 am (UTC)
deflatermouse

Err, yes. Well spotted. *cough*