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

In Spreadsheet::Read Win32::LongPath::openL, I wrote something like:

my $tmp = File::Temp-> new( SUFFIX => '.ods' )-> filename; copyL 'whatever.ods', $tmp; say 'ok' if defined ReadData( $tmp );

which I later realized didn't work exactly as intended. Temp file (and object) no longer existed after 1st statement, copyL simply "hijacked" file name, and temp file wasn't cleaned-up at program end.

Initially I wanted it shorter as

copyL 'whatever.ods', my $tmp = File::Temp-> new( SUFFIX => '.ods' )-> + filename; say 'ok' if defined ReadData( $tmp );

it wasn't "ok" for reason stated above. Instead, in retrospect, this would work:

copyL 'whatever.ods', my $tmp = File::Temp-> new( SUFFIX => '.ods' ); say 'ok' if defined ReadData( $tmp-> filename );

Then object stringifies itself in 1st statement, and survives because bound to a variable. From practical point, that's about it all. However, during investigation I was puzzled by unexpected (though maybe not practically useful) behaviour:

use 5.014; use warnings; package Temp; use parent 'File::Temp'; sub new { my $self = shift; $self-> SUPER::new( DIR => '.' ) } sub DESTROY { my $self = shift; say 'global' if ${^GLOBAL_PHASE} eq 'DESTRUCT'; $self-> SUPER::DESTROY } package main; use File::Copy; sub foo { my $h = Temp-> new } die unless -f 'x'; # this file must exist copy 'x', my $fn1 = Temp-> new-> filename or die; copy 'x', my $fn2 = do { Temp-> new }-> filename or die; copy 'x', my $fn3 = do { my $h = Temp-> new }-> filename or die; copy 'x', my $fn4 = foo-> filename or die; say 1 if -f $fn1; say 2 if -f $fn2; say 3 if -f $fn3; say 4 if -f $fn4;

which says "3". All copying completes successfully, and I'd expect objects destroyed and temp files deleted by end of each of 4 statements. But, somehow, object survives if bound to lexical variable in "do" block, but not in case of subroutine call. Why?

Auxiliary question: object was not destroyed during global destruction at program end, but when?

Replies are listed 'Best First'.
Re: File::Temp survival and scope created by "do"
by Eily (Monsignor) on Dec 10, 2018 at 12:33 UTC

    Somehow the lexical variable in the do block gets attached to the upper scope. As shown with the test below:

    use 5.014; use warnings; package Temp; use parent 'File::Temp'; sub new { my $self = shift; $self-> SUPER::new( DIR => '.' ) } sub DESTROY { my $self = shift; say "Destruction during ", ${^GLOBAL_PHASE}; $self-> SUPER::DESTROY } package main; use File::Copy; sub foo { my $h = Temp-> new } die unless -f 'x'; # this file must exist { copy 'x', my $fn1 = Temp-> new-> filename or die; copy 'x', my $fn2 = do { Temp-> new }-> filename or die; copy 'x', my $fn3 = do { my $h = Temp-> new }-> filename or die; copy 'x', my $fn4 = foo-> filename or die; say 1 if -f $fn1; say 2 if -f $fn2; say 3 if -f $fn3; say 4 if -f $fn4; say "End of scope"; } say "End of file"; __END__ Destruction during RUN Destruction during RUN Destruction during RUN 3 End of scope Destruction during RUN End of file
    This works however:
    copy 'x', my $fn3 = do { { my $h = Temp-> new } }-> filename or die;
    because the extra block creates a lexical scope inside the do block.

    The documentation for do states that it does not work as a loop, when all other blocks (even bare one, without the for or while keyword) do, (which means next and last and redo don't work on do blocks). So somehow do blocks aren't blocks either? What's weird is that there is a "local" scope bound to do, so

    $_ = "Hi"; do { local $_ = "Hello" }; print
    does print; "Hi" as expected...

      Interesting...

      So somehow do blocks aren't blocks either?

      I'm pretty sure a do BLOCK has its own lexical scope:

      $ perl -wMstrict -le 'my $x="A"; do { print $x; my $x="B"; print $x }; print $x' A B A $ perl -wMstrict -le 'do { my $x="A" }; print $x' Global symbol "$x" requires explicit package name (did you forget to d +eclare "my $x"?) at -e line 1. Execution of -e aborted due to compilation errors.

      But it seems to me like this might have something to do with a do block being able to return a value:

      $ perl -wMstrict -le 'sub Foo::DESTROY {print "BLAM"}; print "A"; do { my $x = bless {}, "Foo" }; print "B"' A B BLAM $ perl -wMstrict -le 'sub Foo::DESTROY {print "BLAM"}; print "A"; do { my $x = bless {}, "Foo"; 1 }; print "B"' A BLAM B

      Although I don't know why do { my $x = bless {}, "Foo" } vs. do { bless {}, "Foo" } makes a difference either. Seems strange to me.

        Well it looks like the variable $x (ie, the name) is only visible inside the do BLOCK, but the associated scalar's life is bound to the containing scope.

        I'm not sure what you mean about being able to return a value, but having the inverting the order of the statements inside the do BLOCK yields the same result:

        perl -le "sub Foo::DESTROY {print 'BLAM'}; print 'A'; do { 1; my $x = +bless {}, 'Foo' }; print 'B' "; A BLAM B
        (yup, windows over here). This looks like an optimization issue, when do contains only a single statement, I guess it is optimized as a single statement executed in the containing scope, while the visibility of the variable is still limited to the do block.

        The fact that it works properly when the do contains more than one statement means there isn't a real problem though, because if there's only one statement, the lexical declaration is technically not needed. (Edit: Or maybe the issue is that perl lets you declare a lexical that you never use, but that's not specific to do)