http://www.perlmonks.org?node_id=47633


in reply to typeglobs and filehandles

Says zzspectrez:
> Im confused on how the block can return the value
> of *FH which loses scope once the block exits.
I'm going to explain this in two ways. First, I'll show an analogy to something that you probably do all the time:
sub foo { my $z = 4; return $z; }
How can the block return the value of $z which loses scope once the block exits? But there's no problem here, and you probably never thought that there might be a problem. So why are you worried about returning the value of *FH in the same way?

Second explanation: You have misunderstood the notion of scope. Values don't have scope. Variables don't have scope. Only names have scope.

In the code { my $var; ... }, the name var is in scope inside the block. Outside the block, it is out of scope, and the name has no meaning.

Does the variable lose its value just because the name has gone out of scope? Maybe, but maybe not. Consider this:

my $x; { my $var = 4; $x = \$var; } # 'var' is now out of scope. print $$x; # but this still prints "4".

Here we create a variable, named $var, and set it to 4. Then we take a reference to the variable and store the reference in $x. Then the name var goes out of scope.

Is the variable destroyed? No. Why not? Because in Perl, a variable is never destroyed just because its name has gone out of scope. In Perl, a variable is destroyed when there are no references to it. The name is one reference. Usually it's the only one, and when the name goes away, there are no more references, and the variable is destroyed. In this case, there is a second reference, in $x, so the variable that was formerly known as $var will not be destroyed until the reference in $x is also gone. It's not known as $var any more (because that name is out of scope) but it's not destroyed either.

Scope is the part of the program text in which the name is visible. It's independent of what happens when the program is run. You can tell just from looking at the source code what the scope of each name is. In Perl, the scope of a name begins on the statement following the declaration, and continues up to the end of the smallest enclosing block.

Variables do not have scope. Instead, they have duration. The duration of a variable is the period of time in which memory is allocated for the variable. In Perl, a variable's duration continues until there are no more references to it, at which point it is destroyed.

People often confuse variables with their names, but they are not the same thing. A variable is a part of the computer's memory, and it might or might not have a name. In a program, the same name might refer to different variables at different times.


Now what is my $x = do { local *FH }; doing?

I'm going to remove one of the confusions by rewriting it in an equivalent form:

sub new_filehandle { local *FH; return *FH; } $x = new_filehandle();

What does new_filehandle do? First, it removes the old value that *FH had, if any, and replaces the value of *FH with a new, fresh glob. It arranges for the old value of *FH to be restored at the end of the block.

Then it returns the value of *FH, which as we just saw, is a new, fresh glob. That new, fresh glob is what is assigned to $x.

Then it's the end of the block, so the old value of *FH is restored.

Is the new, fresh glob destroyed? No. Why not? Its duration (not scope) is still going on, because it is referenced by $x.

Now you probably know that return is optional on the last line of a subroutine; Perl will return the final value whether you say return or not. That's why you can write sub pi { 3.14159 }. So we can get rid of return:

sub new_filehandle { local *FH; *FH; # 'return' is implied } $x = new_filehandle();

It turns out that the value of the local is the new value of *FH, so it is not necessary to repeat *FH:

sub new_filehandle { local *FH; # return of *FH is implied } $x = new_filehandle();

Now we can replace the subroutine with a do block, which just executes the code in a block and returns the resulting value:

$x = do { local *FH; }; # return of *FH is implied

Hope this helps.