You may recognize the phrase in the title as Damian Conway’s admonition in Perl Best Practices regarding magic values in your programs. It’s good advice to use names for magic numbers and values to make your code more readable for you and those that have to maintain your code.
But what about the don’t use constant
part? His argument for not
using the pragma essentially boils down to the fact that use
constant
creates package scoped constant subroutines at compile time
vs the Readonly
module which executes at runtime. So we are told to
use Readonly
…fair enough…but Perl::Critic
tells us to use
ReadonlyX
because Readonly
is slow and buggy.
Here’s the problem though: ReadonlyX
is not exactly the same as
Readonly
and many CPAN modules use Readonly
. Case in point:
File::BOM
.
Let’s start with a little script that uses both ReadonlyX
and
File::BOM
;
use strict;
use warnings;
use ReadonlyX;
use File::BOM;
1;
…let’s compile this and see what happens…
perl -wc foo.pl
Subroutine croak redefined at /home/rclauer/lib/perl5/Readonly.pm line 13.
Prototype mismatch: sub Readonly::Scalar ($;$) vs ($$) at /home/rclauer/lib/perl5/Readonly.pm line 257.
Prototype mismatch: sub Readonly::Scalar ($;$) vs ($$) at /home/rclauer/lib/perl5/Readonly.pm line 343.
Subroutine Scalar redefined at /home/rclauer/lib/perl5/Readonly.pm line 316.
Subroutine Array redefined at /home/rclauer/lib/perl5/Readonly.pm line 346.
Subroutine Hash redefined at /home/rclauer/lib/perl5/Readonly.pm line 364.
Subroutine Clone redefined at /home/rclauer/lib/perl5/Readonly.pm line 387.
Prototype mismatch: sub Readonly::Readonly (\[%@$]$) vs (\[$@%]@) at (eval 7) line 42.
Subroutine Readonly redefined at (eval 7) line 1.
foo.pl syntax OK
Hmmm…that looks bad, but I hear that Readonly
and ReadonlyX
should be “drop in replacements for each other? Well then perhaps I
can use ReadonlyX
by telling File::BOM
that Readonly
has already
been loaded…a technique I have used before when these two modules
were conflicting.
use strict;
use warnings;
BEGIN {
use Module::Loaded;
use ReadonlyX;
mark_as_loaded('Readonly');
}
use File::BOM;
1;
…let’s compile this and see if things get better…
perl -wc foo.pl
Useless use of reverse in void context at /home/rclauer/lib/perl5/File/BOM.pm line 204.
Odd number of elements in hash assignment at /home/rclauer/lib/perl5/File/BOM.pm line 187.
Compilation failed in require at foo.pl line 14.
BEGIN failed--compilation aborted at foo.pl line 14.
Nope. It appears that File::BOM
makes use of features or uses
Readonly
in a way that is not compatible with ReadonlyX
. Luckily
my use of ReadonlyX
is compatible with Readonly
, so I can reverse
the technique and use Readonly
and mark ReadonlyX
loaded so that
other modules that use ReadonlyX
will use Readonly
.
use strict;
use warnings;
BEGIN {
use Module::Loaded;
use Readonly;
mark_as_loaded('ReadonlyX');
}
use File::BOM;
1;
Of course, this only works if the downstream modules that use
ReadonlyX
use it in a way that is compatible with Readonly
…and
we’ve just introduced the slow and buggy Readonly
to our stack
which we were trying to avoid by using ReadonlyX
in the first place.
But at least we got our legacy code to work. ;-)