10.14. Redefining a FunctionProblemYou want to temporarily or permanently redefine a function, but functions can't be assigned to. Solution
To redefine a function, assign a new code reference to the typeglob of the name of the function. Use a undef &grow; # silence -w complaints of redefinition *grow = \&expand; grow(); # calls expand() { local *grow = \&shrink; # only until this block exists grow(); # calls shrink() } Discussion
Unlike a variable but like a handle, a function cannot be directly assigned to. It's just a name. You can manipulate it almost as though it were a variable, because you can directly manipulate the run-time symbol table using typeglobs like Assigning a reference to a typeglob changes what is accessed the next time a symbol of that type is needed. This is what the Exporter does when you import a function or variable from one package into another. Since this is direct manipulation of the package symbol table, it only works on package variables (globals), not lexicals. *one::var = \%two::Table; # make %one::var alias for %two::Table *one::big = \&two::small; # make &one::big alias for &two::small
A typeglob is something you can use local *fred = \&barney; # temporarily alias &fred to &barney
If the value assigned to a typeglob is not a reference but itself another typeglob, then
all
types by that name are aliased. The types aliased in a full typeglob assignment are scalar, array, hash, function, filehandle, directory handle, and format. That means that assigning Use assignments to typeglobs together with closures to clone a bunch of similar functions cheaply and easily. Imagine you wanted a function for HTML generation to help with colors. For example:
$string = red("careful here");
print $string;
You could write the sub red { "<FONT COLOR='red'>@_</FONT>" } If you need more colors, you could do something like this: sub color_font { my $color = shift; return "<FONT COLOR='$color'>@_</FONT>"; } sub red { color_font("red", @_) } sub green { color_font("green", @_) } sub blue { color_font("blue", @_) } sub purple { color_font("purple", @_) } # etc
The similar nature of these functions suggests that there may be a way to factor out the common bit. To do this, use an assignment to an indirect typeglob. If you're running with the highly recommended @colors = qw(red blue green yellow orange purple violet); for my $name (@colors) { no strict 'refs'; *$name = sub { "<FONT COLOR='$name'>@_</FONT>" }; }
These functions all seem independent, but the real code was in fact only compiled once. This technique saves on both compile time and memory use. To create a proper closure, any variables in the anonymous subroutine
must
be lexicals. That's the reason for the This is one of the few places where giving a prototype to a closure is sensible. If you wanted to impose scalar context on the arguments of these functions (probably not a wise idea), you could have written it this way instead: *$name = sub ($) { "<FONT COLOR='$name'>$_[0]</FONT>" }; However, since prototype checking happens at compile time, the preceding assignment happens too late to be useful. So, put the whole loop of assignments within a BEGIN block, forcing it to occur during compilation. See AlsoThe sections on "Symbol Tables" in Chapter 5 of Programming Perl and in perlmod (1); the section on "Closures" in Chapter 4 of Programming Perl and the discussion of closures in perlref (1); Recipe 10.11 ; Recipe 11.4 |
|