The trite answer is that you have to use local
because my *FH
is a syntax error.
The "right" (but not very enlightening) answer is that you're doing it wrong. You should be using lexical filehandles and the three-argument form of open
instead.
sub newopen {
my $path = shift;
my $fh;
open($fh, '<', $path) or do {
warn "Can't read file '$path' [$!]\n";
return;
}
return $fh;
}
To really answer why requires an explanation of the difference between lexical and global variables and between a variable's scope and its duration.
A variable's scope is the portion of the program where its name is valid. Scope is a static property. A variable's duration, on the other hand, is a dynamic property. Duration is the time during a program's execution that the variable exists and holds a value.
my
declares a lexical variable. Lexical variables have a scope from the point of declaration to the end of the enclosing block (or file). You can have other variables with the same name in different scopes without conflict. (You can also re-use a name in overlapping scopes, but don't do that.) The duration of lexical variables is managed thorugh reference counting. So long as there is at least one reference to a variable the value exists, even if the name isn't valid within a particular scope! my
also has a runtime effect -- it allocates a new variable with the given name.
local
is a bit different. It operates on global variables. Global variables have a global scope (the name is valid everywhere) and a duration of the entire life of the program. What local
does is make a temporary change to the value of a global variable. This is sometimes referred to as "dynamic scoping." The change starts at the point of the local
declaration and persists until the end of the enclosing block after which the old value is restored. It's important to note that the new value is not restricted to the block -- it is visible everywhere (including called subroutines). Reference counting rules still apply, so you can take and keep a reference to a localized value after the change has expired.
Back to the example: *FH
is a global variable. More accurately it's a "typeglob" -- a container for a set of global variables. A typeglob contains a slot for each of the basic variable types (scalar, array, hash) plus a few other things. Historically, Perl used typeglobs for storing filehandles and local
-izing them helped ensure that they didn't clobber each other. Lexical variables don't have typeglobs which is why saying my *FH
is a syntax error.
In modern versions of Perl lexical variables can and should be used as filehandles instead. And that brings us back to the "right" answer.