12.13.3. Discussion
The technique demonstrated in the previous recipe only overrides a
built-in in a particular package. It doesn't change everything for
your whole program, no matter what package that function is called
from. To do so would risk changing the behavior of code from modules
you didn't write, and which were therefore not prepared for the
change.
It has been said that Unix was not designed to stop you from doing
stupid things, because that would also stop you from doing clever
things. So, too, with Perl. Just because overriding a function in all
packages at once might seem, well, imprudent doesn't mean a clever
person won't someday find a marvelous use for such a facility.
For example, let's suppose that you've decided that the core
int function's behavior of integer truncation,
also known as rounding toward zero, is so annoying to your program
that you want to provide an alternative by the same name. This would
do it:
package Math::Rounding;
use warnings;
use Carp;
use Exporter;
our @EXPORT = qw(int);
our @ISA = qw(Exporter);
sub int(;$) {
my $arg = @_ ? shift : $_;
use warnings FATAL => "numeric"; # promote to die( )ing
my $result = eval { sprintf("%.0f", $arg) };
if ($@) {
die if $@ !~ /isn't numeric/;
$@ =~ s/ in sprintf.*/ in replacement int/s;
croak $@;
} else {
return $result;
}
}
Your replacement version uses sprintf( ) to round
to the closest integer. It also raises an exception if passed a
non-numeric string. A program could access this function either by
saying:
use Math::Rounding ( );
$y = Math::Rounding::int($x);
or by importing the function and overriding the built-in:
use Math::Rounding qw(int);
$y = int($x);
However, that only manages to replace the built-in for the current
package. To replace it in all packages, at some point during compile
time you'll have to execute a line of code like this:
*CORE::GLOBAL::int = \&Math::Rounding::int;
The standard File::Glob module allows you to change Perl's core
glob operator using special import tags:
## override the core glob, forcing case sensitivity
use File::Glob qw(:globally :case);
my @sources = <*.{c,h,y}>
## override the core glob forcing case insensitivity
use File::Glob qw(:globally :nocase);
my @sources = <*.{c,h,y}>
The module does this with its own version of
import that detects those tags and makes the
necessary assignments. You could do this, too. That way, this:
use Math::Rounding qw(-global int);
would make Perl use your replacement version for all calls to
int from any package anywhere in your program.
Here's a replacement import function that handles
this:
sub import {
if (@_ && $_[1] =~ /^-/) {
if ($_[1] ne "-global") {
croak "unknown import pragma";
}
splice(@_, 1, 1); # discard "-global"
no warnings "once"; # suppress "used only once" warnings
*CORE::GLOBAL::int = \∫
} else {
die;
}
_ _PACKAGE_ _ -> export_to_level(1, @_);
}
The assignment happens only if the first thing to import is
"-global". The last line in our
import function uses part of the Exporter module's
internal API to handle any normal import.