jettero has asked for the wisdom of the Perl Monks concerning the following question:

I'm looking for ways to avoid the fact that an anonymous code ref usually references itself (and therefore never goes out of scope). Leonard of #perl-freenode complained that a sub like this will never say goodbye:

my $x = sub { "test" }; bless $x, "HRM"; sub HRM::DESTROY { print "bye\n" }

I used Devel::FindRef to discover the obvious: the sub keeps a reference to $x for closure reasons, which makes sense. If you construct the ref like this:

my $y = eval 'sub {"test"}'; bless $y, "HRM";

... It will say goodbye just fine. Are there other, hopefully non-eval, ways to keep the sub from referencing itself? (It's not a bug is it? I mean, you clearly want the subs to keep references to all the lexicals they see... just not ... that one, sometimes.)

-Paul

Replies are listed 'Best First'.
Re: coderefs keep references to themselves
by moritz (Cardinal) on Jul 15, 2008 at 11:43 UTC
    On #p5p Leonard came up with working example of taking a closure. My version of it is this:
    sub Foo::DESTROY { print "foo::DESTROY called\n"; } { my $x = do {my $var; bless sub {$var}, 'Foo'; }; } __END__ foo::DESTROY called

    The fact that a simple sub { "test" } isn't garbage collected is an optimization that's poorly hidden from the user. It basically means that perl doesn't have to recompile a sub. Since eval can't be cached anyway it doesn't make sense to perform that optimization there.

      Oh, I see. Since the simple sub never references anything outside of it, meaning it'd not change and wouldn't need to be recompiled, the optimizer preserves it with refcount trickery? That actually makes perfect sense and answers my question below the response below.

      -Paul

Re: coderefs keep references to themselves
by dave_the_m (Monsignor) on Jul 15, 2008 at 11:44 UTC
    Actually its the other way round; the destroy doesn't happen because its not a closure. For non-closure anonymous subs, perl just shares the same internal CV structure among all the code refs to the sub

    As soon as you make the sub a closure (by referring to an external lexical var), it starts to behave the way you expect:

    $ perl -e 'my $z; my $x = sub { $z; "test" }; bless $x, "HRM"; sub HR +M::DESTROY { print "bye\n" };' bye $

    Dave.

      Curious. I definitely don't understand how using $z in $x makes $x point to $x less.

      -Paul

        It's because there's a reference to a variable in the enclosing scope ($z) that it's actually now a closure. Before it had no ties to any variables in the enclosing scope so it wasn't a closure triggering the optimization. Adding $z means that it now does reference a var in the enclosing scope and won't trigger the optimization and now the destructor is called. If yours (or his) had referenced $x (although you'd have to make the declaration and assignments two steps (e.g. my $x; $x = sub { $x; "test" })) then $x would have served the same purpose; as is $x never enters into the closure/not-closure picture because your lexical $x isn't referenced from your anonymous sub.

        The cake is a lie.
        The cake is a lie.
        The cake is a lie.