Modules · Language · 1.04

Const::XS

Read-only scalars, arrays and hashes, all the way down, in XS.

const my %config => (...) sets a variable and makes it read-only, with every array and hash inside it. A key that was never set is an error rather than a quiet undef, so a typo in a constant's name fails where it is made.

use Const::XS qw(const);

const my %config => (
    workers => 5,
    hosts   => [qw(a.example b.example)],
    retry   => { times => 3, wait => 2 },
);

print $config{retry}{times}, "\n";             # 3

# read-only all the way down: the nested array and hash too
eval { push @{ $config{hosts} }, 'c.example' };
print "refused: $@";                           # Modification of a read-only value attempted

eval { $config{retry}{times} = 10 };
print "refused: $@";

# and a key that was never set is an error, not a quiet undef
eval { my $port = $config{port} };
print "refused: $@";                           # Attempt to access disallowed key 'port'
print exists $config{port} ? '' : "no port\n"; # exists still asks safely

What you get

  • Deeply read-only

    An array or hash made with const is read-only, and so is every array and hash inside it. A push, a store or a delete anywhere in the structure dies.

  • Missing keys are errors

    Reading a key a constant hash does not have dies, naming the key, and exists still asks safely. A misspelt constant fails where it is used, not three calls later.

  • Any variable, after the fact

    make_readonly locks a variable that already exists, unmake_readonly opens it again, and is_readonly says which it is.

  • A read-only copy

    make_readonly_ref returns a read-only copy of a structure in a variable that can still be rebound.

  • Shared between packages

    Declare const our values in one package and export them, so every module reads the same values and none of them can change one.

In use

use Const::XS qw(make_readonly unmake_readonly is_readonly make_readonly_ref);

# an existing variable, made read-only in place, and back again
my %limits = (upload => 1_048_576, requests => 100);
make_readonly(%limits);
print is_readonly(%limits) ? "locked\n" : "open\n";      # locked

unmake_readonly(%limits);
$limits{requests} = 200;
print "requests: $limits{requests}\n";                    # 200

# a read-only copy of a structure, in a variable that can still be rebound
my $defaults = make_readonly_ref({ theme => 'dark', lang => 'en' });
eval { $defaults->{theme} = 'light' };
print $@ ? "the copy is read-only\n" : '';
$defaults = { theme => 'light' };                         # the variable itself is not
print "$defaults->{theme}\n";                             # light

Measured in its POD

One run, as the POD records it, with no machine named: making a read-only string, hash and array and reading them back. Const::XS did 1,275,510 a second, Const::PP 254,194, Const::Fast 221,828 and Readonly 143,720, so Const::XS was 5.8 times Const::Fast and 8.9 times Readonly.

Get it

cpanm Const::XS