Beefy Boxes and Bandwidth Generously Provided by pair Networks
Clear questions and runnable code
get the best and fastest answer

Know Your Garbage -- A Quiz

by dws (Chancellor)
on Jun 18, 2004 at 05:13 UTC ( #367864=perlmeditation: print w/replies, xml ) Need Help??

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 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";

Replies are listed 'Best First'.
Re: Know Your Garbage -- A Quiz
by blokhead (Monsignor) on Jun 18, 2004 at 05:49 UTC
    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! ;)


      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.
Re: Know Your Garbage -- A Quiz
by PodMaster (Abbot) on Jun 18, 2004 at 05:43 UTC
    I say
    • Weell $one is sure to go first.
    • $three is sure to go right after $four is created.
    • "five" is sure to be printed before $four (or $two) gets cleaned up. I'm not sure the order in which $four and $two will be destroyed (because perl doesn't guarantee order of destruction), but I vaguely recall something about closures going away last, so that's why i'm going with that answer.

      update2: er, actually, forget what i said about closures. two goes before four because the closure is invoked (duh). So I guessed right, but by the time I got to writing down why, things got fuzzy :)(i rushed)

    update: I just checked on my perl v5.6.1 built for MSWin32-x86-multi-thread (ActivePerl Build 638), and I guessed correctly :)

    MJD says "you can't just make shit up and expect the computer to know what you mean, retardo!"
    I run a Win32 PPM repository for perl 5.6.x and 5.8.x -- I take requests (README).
    ** The third rule of perl club is a statement of fact: pod is sexy.

Re: Know Your Garbage -- A Quiz
by ysth (Canon) on Jun 22, 2004 at 15:16 UTC
    Code works as I would expect beginning with 5.9.0. A different order is output in versions 5.6.2, 5.8.[1234]. Putting a line:
    scalar $two;
    next to the my $three line makes all versions work consistently.

Log In?

What's my password?
Create A New User
Node Status?
node history
Node Type: perlmeditation [id://367864]
Front-paged by mojotoad
and all is quiet...

How do I use this? | Other CB clients
Other Users?
Others taking refuge in the Monastery: (2)
As of 2017-08-22 01:04 GMT
Find Nodes?
    Voting Booth?
    Who is your favorite scientist and why?

    Results (326 votes). Check out past polls.