ETOOBUSY 🚀 minimal blogging for the impatient
Readonly::Tiny
TL;DR
Readonly::Tiny is better than constant.
UPDATE 2021-10-04 it was long time due to fix the big issue with this post! Now it does what it was meant to do.
I rarely use constants, mainly because I like those constants to be… variables that are possibly read from a file. But yes, those variables might have default values and these would probably be… constants.
The canonical way to declare a constant value in Perl is this:
use constant SOME_VALUE => 42;
This actually creates a sub like this:
sub SOME_VALUE() { 42 }
Being created at compile time (it’s done with a use
for a reason) and
having an empty prototype means that it can be called without parentheses,
so this:
my $value = shift // SOME_VALUE;
does what you think.
But this interface is… not optimal. For example, you cannot use this value in interpolated stuff; as an example, these two lines print different things:
print 'default value is ', SOME_VALUE, "\n";
print "default value is SOME_VALUE\n"
and this does not expand the value in the constant either, at least on the left of the fat comma:
my %doubles = (SOME_VALUE => SOME_VALUE * 2);
There are a couple workarounds for this:
# SOME_VALUE is a function after all, let's call it as such
my %doubles = (SOME_VALUE() => SOME_VALUE * 2);
# force evaluation as a string
my %triples = (SOME_VALUE.'' => SOME_VALUE * 3);
But they are… workarounds, and they don’t work inside double-quoted strings anyway.
The solution to this would be to use a full-fledged scalar, but scalars are variables, not constants.
Well… not so fast! Scalars can be constants… and Readonly::Tiny can help us craft them as such:
use Readonly::Tiny 'Readonly';
Readonly my $SOME_VALUE => 42;
Now things will work as expected:
print "default value is $SOME_VALUE\n";
my %doubles = ($SOME_VALUE => $SOME_VALUE * 2);
# the following statement will die with error message:
# Modification of a read-only value attempted...
$SOME_VALUE = 37;
Last, if you don’t like using the fat-comma to do what amounts to an
assignment (another itch I always had with use constant
), it’s
possible to use function readonly
instead, taking care to pass a
reference to the variable we want to make read-only:
use Readonly; # function `readonly` is exported by default, yay!
readonly \(my $SOME_VALUE = 42);
So… if you are in need for a scalar constant, keep Readonly::Tiny in mind!