Credit: Piledhigheranddeeper at English Wikipedia, CC BY-SA 3.0 , via Wikimedia Commons

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. ;-)


Next post: ChatGPT Don’t Refactor Without It

Previous post: Go Ahead ‘make’ My Day