Inspired by a bug I watched [kragen] track down today, here's a quiz: Without running the program below, describe the output you expect, and explain why.
See [id://38538|How to present spoilers :-)] for instructions on how to obscure your response.
Updated to add a bit more challenge: If the comment is removed, does the order change? Why (or why not)?
package OnDestroy;
sub say {
my ($class, $msg) = @_;
bless { msg => $msg }, $class;
}
sub DESTROY {
my ($self) = @_;
print $self->{msg}, "\n";
}
package main;
{
my $one = OnDestroy->say("one");
}
{
my $two = OnDestroy->say("two");
sub closure {
my ($four) = @_;
my $three = OnDestroy->say("three");
sub { $two = $four }
}
}
my $four = closure(OnDestroy->say("four"));
$four->();
# undef $four;
print "five\n";
I say
one three five two fourWhy?
|
update: I just checked on my perl v5.6.1 built for MSWin32-x86-multi-thread (ActivePerl Build 638), and I guessed correctly :)
|
Update: On my Perl 5.8.4 i386-linux-thread-multi, this outputs
| one three five four two |
Update 2: dws tells me my guess is what he expected to happen, and what happens for him in 5.8.0. Bizarro!
Looks like perltoot says it all!
Perl's notion of the right time to call a destructor is not well-defined currently, which is why your destructors should not rely on when they are called.Therefore I declare any permutation of one through five to be technically correct. I love happy endings! ;)
blokhead
|
Try printing $two->{msg} at the beginning of the anonymous sub (with warnnings on.). Here, on 5.8.3 I get an uninitialized value. Also by printing the stringified \$two in closure(), and in the anon sub, I get different strings. I find a suitable explanation in perldiag: Variable "%s" may be unavailable (W closure) An inner (nested) anonymous subroutine is inside a named subroutine, and outside that is another subroutine; and the anonymous (innermost) subroutine is referencing a lexical variable defined in the outermost subroutine. For example: sub outermost { my $a; sub middle { sub { $a } } } If the anonymous subroutine is called or referenced (directly or indirectly) from the outermost subroutine, it will share the variable as you would expect. But if the anonymous subroutine is called or referenced when the outermost subroutine is not active, it will see the value of the shared variable as it was before and during the *first* call to the outermost subroutine, which is probably not what you want. In these circumstances, it is usually best to make the middle subroutine anonymous, using the sub {} syntax. Perl has specific support for shared variables in nested anonymous subroutines; a named subroutine in between interferes with this feature. Only here, we have just a scope rather than a named outer sub, and no warning is given. But, as it appears, $two isn't replaced in the anon sub, and therefore lasts after the end of execution, then five is printed, and four and two are collected in some order. (I get : one, three, five, two, four). If you replace sub closure with our $closure=sub, and closure() with $::closure->(), $two remains shared, and I get : one,three,two,five,four. |
scalar $two;next to the my $three line makes all versions work consistently.
perlmonks.org content © perlmonks.org and blokhead, dws, Ido, PodMaster, ysth
prlmnks.org © 2006 edmund von der burg (eccles & toad)
v 0.03