in reply to Know Your Garbage -- A Quiz

Neat puzzle!

  • $one is created and destroyed as we leave its scope, outputting one

  • $two is created but kept alive outside its block by the reference within closure

  • OnDestroy->say("four") is created and passed into closure

    • It gets assigned to $four

    • $three gets created

    • As we pass out of this scope, $four stays alive because of the reference in the newly created anonymous sub. There are no more references left to $three though, so it gets destroyed, outputting three

  • Now things get confusing ;) The anonymous sub executes, and the value of $two is replaced. Its old value gets destroyed, outputting two .. Its new value (same as the value in $four) stays around for the same reason $two is staying around in the first place.

  • The program prints five

  • The program ends, and the value in $two is finally garbage-collected. Due to the previous execution of that anonymous closure, the value of $two is the object which prints four!

Update: On my Perl 5.8.4 i386-linux-thread-multi, this outputs

one three five four two
which is different than PodMaster's output (probably due to undefined order of garbage collection at the end of the program), and certainly different from my guess. There's something fishy going on with the $two variable, I bet! Hmm.. Is the GC being lazy and deferring some destructions?

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

Replies are listed 'Best First'.
Re^2: Know Your Garbage -- A Quiz
by Ido (Hermit) on Jun 19, 2004 at 13:16 UTC
    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.