CPAN modules for defining constants

other reviews

Neil Bowers

2012-08-10

This is a review of CPAN modules that can be used to define or work with constants. The following modules are covered:

Module Doc Version Author # bugs # users Last update
Attribute::Constant CPAN 0.02 Dan Kogai 1 1 2008-06-27
Config::Constants CPAN 0.03 Stevan Little 0 0 2005-05-05
Const::Fast CPAN 0.012 Leon Timmermans 1 20 2012-08-09
constant CPAN 1.21 Sébastien Aperghis-Tramoni 0 221 2011-04-18
constant::def CPAN 0.01 Mons Anderson 0 1 2009-03-11
constant::defer CPAN 5 Kevin Ryde 0 8 2011-02-11
Constant::FromGlobal CPAN 0.01 יובל קוג'מן (Yuval Kogman) 1 1 2009-11-05
Constant::Generate CPAN 0.16 Mark (מרדכי) Nunberg 0 5 2012-01-30
constant::lexical CPAN 2.0001 Father Chrysostomos 0 4 2012-01-08
constant::our CPAN 0.07 Evdokimov Denis 0 0 2012-05-14
Devel::Constants CPAN 1.01 Neil Bowers 0 0 2012-02-01
enum CPAN 1.016 Byron Brummer 2 13 1999-05-27
enum::fields CPAN 1.0 David Lloyd 0 1 2002-07-27
ex::constant::vars CPAN 0.03 Neil Bowers 0 0 2012-01-28
Lexical::Import CPAN 0.002 Andrew Main (Zefram) 0 0 2011-10-26
Lexical::Var CPAN 0.007 Andrew Main (Zefram) 0 3 2012-02-04
Readonly CPAN 1.03 Eric J. Roode 18 269 2004-04-21
Readonly::XS CPAN 1.05 Eric J. Roode 7 5 2009-02-24
Scalar::Constant CPAN 0.001005 Arun Prasaad 1 0 2012-01-29
Scalar::Construct CPAN 0 Andrew Main (Zefram) 1 0 2012-02-06
Scalar::Readonly CPAN 0.03 Philippe M. Chiasson 2 1 2012-01-23

In one of my projects I was using Readonly, but hit a snag, so ended up doing this review, to work out which module I should switch to. The funny thing is, that as a result of writing this, I had to learn more about Readonly, and now I can't remember what the problem was!

For each module I wrote a simple script, in the style of the SYNOPSIS section of documentation. I tried to make these the same, as much as possible. The relevant interesting part of these is included to illustrate the module.

I also wrote a script to test every module's constantness, using Test::More to check that constants really were immutable.

use Const::Fast;
use Test::More;

const my $scalar => 42;
eval { $scalar = 15; };
ok($@, "assigning to my scalar");

After reviewing each module, various comparisons are presented, and then recommendations given for which module to use when.

At first I included module constant::Atom in this review, but it's really for defining an enumeration, and I discovered there are other similar modules. I haven't included them in this review — maybe they'll get one of their own.

Attribute::Constant

Attribute::Constant lets you define scalar, hash and array constants by defining a Constant attribute. You can use it with both my and our variables.

use Attribute::Constant;

my $scalar  : Constant(42);
my @array   : Constant(qw(red green blue));
my %hash    : Constant(food => 'sushi', drink => 'sake');

our $string : Constant('xyzzy');
our @list   : Constant(17, 23);
our %map    : Constant(x => 1, y => 2, z => 3);

The initialiser can be empty, or even omitted. For a scalar this results in undef, for an array or hash it produces the empty list.

Internally, Attribute::Constant uses dlock() from Data::Lock, which is part of the same distribution. The dlock() function uses the the core undocumented Internals::SvREADONLY() function to make the variable read-only. This is true even if the value is a complex data structure (as with Const::Fast, see the example in that section below).

This is clean and fairly simple, thought the syntax feels a little strange for declaring a constant. It's only version 0.02 (released 2008), which might be interpreted as unstable and likely to change, but in fact there's not much more the module needs.

Config::Constants

Config::Constants lets you define scalar constants in a config file, and then provides function constants for referring to the values in the config file. Let's say you want to create a separate module, Foobar::Constants, which will provide all constants for use by the rest of your code.

First, you create the XML which defines the name and value of each constant:

<config>
    <module name="Foobar::Constants">
        <constant name="STATE" value="bewildered"/>
    </module>
</config>

Then your constants class might look something like:

package Foobar::Constants;

use Config::Constants xml => 'constants.xml';

use parent qw(Exporter);
our @EXPORT = qw(STATE);

1;

If a script wants to use a constant, it just does:

use Foobar::Constants;

print "STATE has value ", STATE, "\n";

It took me a little while to get my head around this module — I found the SYNOPSIS in the documentation a little confusing. I get the feeling it was an experiment, one that hasn't been taken forward. That said, I like the idea of configuration constants being in a config file.

Const::Fast

Const::Fast lets you define scalar, hash and array constants with the const function, which is exported by default. You can use it with both my and our variables.

use Const::Fast;

const my $scalar  => 42;
const my @array   => qw(red green blue);
const my %hash    => (food => 'sushi', drink => 'sake');

const our $string => 'xyzzy';
const our @list   => (17, 23);
const our %map    => (x => 1, y => 2, z => 3);

If the value of a constant is a complex data structure, or reference to one, then the entire structure will be made read-only. For example:

const my $href =>
{
    color => {red => 100, green => 127, blue => 64},
    size  => 'large',
};

$href->{color}->{red} = 200;

results in the error:

Modification of a read-only value attempted at deep.pl line 12.

Const::Fast uses the core undocumented Internals::SvREADONLY() function to make the specific items read-only. Since it's undocumented, I had a look at the source, which made me smile:

  XS(XS_Internals_SvREADONLY) /* This is dangerous stuff. */

constant

The constant pragma is used to define function constants at compile time.

use constant SCALAR => 42;
use constant LIST   => qw(red green blue);

print "scalar  = ", SCALAR,    "\n";
print "list[1] = ", (LIST)[1], "\n";

When you declare a constant this way, the value is evaluated at compile time, and assigned to the constant. Wherever the constant is used in an expression, it is replaced by the constant's value.

With this module you can define scalar and list constants. Note that the LIST constant above is a list, not an array, which is why it appears in parentheses above.

The documentation for the pragma includes the following example:

  use constant PI => 4 * atan2(1, 1);

If you use a function in the value of a constant, the function must be defined before you define the constant.

One of the key uses for this type of constant is conditional compilation:

  use constant DEBUG => 0;

  sub foobar {
    print "starting foobar()\n" if DEBUG;
    # ...
  }

Where a constant is used in an if (CONSTANT) construct, the code will be optimized away at compile time if the constant evaluates to false.

One thing to beware of: if you define string constants, you can't use it in the obvious way as a hash key:

  $color = $color_hash{BACKGROUND};

Here BACKGROUND will be treated as a bareword. To force the interpretation as a constant, you have to use one of several ways to prevent the bareword, such as treating it as a function:

  $color = $color_hash{BACKGROUND()};

As the documentation points out, you could assign a hash ref to a constant, but the referenced hash isn't immutable:

use constant HASHREF => { color => 'red', size => 'large' };
print "color = ", HASHREF->{color}, "\n";
HASHREF->{color} = 'blue';
print "color = ", HASHREF->{color}, "\n";

Output:

color = red
color = blue

If you look at Const::Fast, if you create a scalar constant which is actually a reference to a deep data structure, it will make the entire data structure read-only. I wonder if there's any reason why the constant pragma couldn't borrow that code?

constant::def

This distribution contains two modules: constant::def and constant::abs. These let you define a constant in a module, and effectively override it from another part of your code.

Let's say you have a module Foobar. In this module you want to define a constant DEBUG, unless someone else has already defined the constant in your namespace.

package Foobar;
use constant::def DEBUG => $ENV{FOOBAR_DEBUG} || 0;

sub do_something
{
    print "in do_something()\n" if DEBUG;
}

And then in a script which is using the module, you want to specify a value for the constant, which effectively overrides the module's own preferred value:

use constant::abs 'Foobar::DEBUG' => 1;
use Foobar;

Foobar::do_something();

Note that you need to use constant::abs before you use the module that you're defining the constant in.

As with the constant pragma, there is no way to use these modules for a constant hash. See the section on constant above.

I can't see the scenario where these modules would be the right way to define constants. If you have a collection of your own modules which need to work together, why not have a module which defines and exports constants? And if you're using a third-party module, the odds are pretty slim that it's using constant::def. And the need to use constant::abs before use'ing the third-party module just seems a bit clumsy. But maybe I'm missing the killer scenario?

constant::defer

constant::defer is used to define a constant in a way very similar to the constant module, but the value must be an anonymous sub. The first time the constant is used, the sub is called, and the constant is then redefined as a function constant with the value returned by the sub.

use constant::defer FIB5 => sub {
                                  print "BOO!\n";
                                  return fibonacci(5);
                                };

print "fib(5) = ", FIB5, "\n";
print "fib(5) = ", FIB5, "\n";

Running this you'll get:

  BOO!
  fib(5) = 5
  fib(5) = 5

As with constant, this can be used for scalar and list constants.

The main purpose for this is obviously if the constant value is expensive to produce. Maybe it is retrieved from somewhere, such as a database, or calculated. I haven't hit such a situation myself, but I'm guessing the author did, to have created this.

The documentation also suggests that you could use it to create one instance of a class, which is then re-used. Later on the doc references Class::Singleton, which is probably a better way to do that. The documentation also lists other modules which can be used for lazy (or deferred) calculation. Not enough modules provide these sorts of pointers, so +1 for the author Kevin Ryde.

I can't think of a situation where this would be the right module, but now it's in my mental arsenal, maybe I'll come across one in the future.

Constant::FromGlobal

Constant::FromGlobal lets you define scalar function constants which take their values from one of three places, in order of priority:

The module was prompted by a blog post by Adam Kennedy, where he defines the "Constant Global" pattern. His canonical example is for setting debug mode in a module:

BEGIN { our $DEBUG = 1; }
use Constant::FromGlobal qw(DEBUG);

print "in debug mode\n" if DEBUG;

Note that because the constant is defined at compile time, the associated variable must be declared inside a BEGIN block.

This means you could have a performance-sensitive module which most of the time has debug statements compiled out. But then if you need to turn on debug for that module, you can use:

  BEGIN { $HotSpot::DEBUG = 1; }
  use HotSpot;

Obviously the module needs to be cooperating, by using Constant::FromGlobal.

If there isn't a package global of the right name, Constant::FromGlobal will look for a suitably named environment variable, if you tell it to:

BEGIN { $ENV{MAIN_DEBUG} = 1; }
use Constant::FromGlobal DEBUG => { env => 1 };

print "in debug mode\n" if DEBUG;

The environment variable must include the full namespace, with :: changed to underscore.

You can also specify a default value, which will be used if there isn't a global variable, and the environment variable wasn't used (or there wasn't an environment variable set):

use Constant::FromGlobal LOG_LEVEL => { env => 1, int => 1, default => 2 };

print "Log level = ", LOG_LEVEL, "\n";

That also illustrates the int control, which says that the constant must have an integer value, enforced by looks_like_number() from Scalar::Util.

Constant::Generate

Constant::Generate is the swiss army module for defining function constants. It can be used to generate a number of different styles of constants, and has various bells & whistles on top.

First, here's the basic definition of constants with a value:

use Constant::Generate
    {
        HAPPY       => 17,
        INDIFFERENT => 23,
        SAD         => 42,
    };

Unlike constant and similar modules, you can't define list constants, only scalar. And like constant and other modules described above, you can't create a true hash-ref or array-ref constant.

If you just pass symbol names, they will be assigned values in sequence:

use Constant::Generate [qw(ALPHA BETA GAMMA)];
print "ALPHA = ", ALPHA, "\n";
print "BETA  = ", BETA,  "\n";
print "GAMMA = ", GAMMA, "\n";

Which produces the output:

ALPHA = 0
BETA  = 1
GAMMA = 2

By default any generated constants are sequential integers. You can change this behaviour by passing the type option when declaring constants. If you pass 'str', then the constants are given string values, with each constant's string value being the name of the constant. The type 'bit' will create bit-flag constants:

use Constant::Generate [qw(PERL RUBY LISP)], type => 'bit';
print "PERL = ", PERL, "\n";
print "RUBY = ", RUBY, "\n";
print "LISP = ", LISP, "\n";

Which produces the output:

PERL = 1
RUBY = 2
LISP = 4

For integer and bit-flag constants, you can specify the value for the first constant, using the start_at option. You can also request a reverse-mapping function (c.f. Devel::Constants) with the mapname option:

use Constant::Generate [qw(LEGO TRIO OCTO)], type => 'bit', mapname => 'toys2str';
$toys = LEGO | TRIO;
print "toys = ", toys2str($toys), "\n";

Which produces the output:

toys = LEGO|TRIO

You can also request that the constants be defined as dualvars: in numeric context the constant will have the value which either you or the module assigned it. In a string context its value will be the name of the constant:

use Constant::Generate [qw(COFFEE TEA HOTCHOC)], dualvar => 1;
$value = TEA;
printf "value(int) = %d\n", $value;
printf "value(str) = %s\n", $value;

Which produces the output:

value(int) = 1
value(str) = TEA

There are two ways two define dualvar constants: either with the dualvar option, or by using Constant::Generate::Dualvar instead of Constant::Generate.

The documentation refers to dualvar constants as DWIM constants, though I don't get why dualvar implies DWIM.

Constant::Generate also provides support for the scenario which started me doing this review in the first place: creating a module which defines and exports constants. Your constant module is written as follows:

package MyConstants;
use parent qw(Exporter);
our @EXPORT;
use Constant::Generate {
        TITLE  => 1,
        AUTHOR => 2,
        ISBN   => 3,
    }, export => 1;

1;

You can specify export_ok instead of export.

There are plenty more features I haven't touched on, including the allvalues and allsyms options, and the prefix option.

constant::lexical

constant::lexical is used to define constants in a very similar way to the constant module, but makes them local to the enclosing scope.

use constant::lexical LOG_LEVEL => 1;

print "log level = ", LOG_LEVEL, "\n";

{
 use constant::lexical LOG_LEVEL => 3;
 print "in nested scope, log level = ", LOG_LEVEL, "\n";
}

print "after nested scope, log level = ", LOG_LEVEL, "\n";

Which when run, produces:

  log level = 1
  in nested scope, log level = 3
  after nested scope, log level = 1

Father Chrysostomos says he created this module because he wanted to use function constants, but didn't want them appearing to be methods for his classes. It's a shame the constant folding doesn't work for readonly scalars.

constant::our

constant::our implements a kind of global or shared constant, using the constant pragma to create the constants. The one-line description in the NAME section of the pod says "pragma to declare constants like our vars". After reading the documentation, I wasn't exactly sure what the module would do for me, so I had to read the code to be sure.

There are two ways you use this module: at the top-level, for example in package main, you use this module to declare one or more constants, and give them values. In other packages you just give the names of the constants, and they're imported into your namespace:

package main;
use constant::our { FOOBAR => 17 };

package Foobar;
use constant::our qw(FOOBAR BANANA);
sub printit
{
    print "FOOBAR = ", FOOBAR, "\n";
    if (defined(BANANA)) {
        print "BANANA = ", BANANA, "\n";
    } else {
        print "BANANA not defined\n";
    }
}

package main;
Foobar::printit();

So far, it looks like it largely works like constant. But it doesn't. When you run:

package main;
use constant::our { FOOBAR => 17 };

You might think that you're effectively creating a sub main::FOOBAR, but what it actually does is:

And then, when in another module you write:

package Foobar;
use constant::our qw(FOOBAR BANANA);

It looks to see if the constant has been defined in constant::our; if it has then it's exported. If it's not exported (as with BANANA in the example above), then a new constant function is declared, returning undef. This means that you can refer to constants that haven't been defined yet.

The end result of this mechanism is that you must define any constants you want to refer to, before you refer to them anywhere. And if you have some constants that you want to use in a lot of modules, you have to refer to the constant names explicitly in every module.

The mechanism used also means that this module can only be used to define scalar constants. You can't use it for LIST constants, because let's say you tried the following:

use constant::our FOOBAR => (1, 2, 3);

The import method knows how to deal with two cases: the first is a hashref, which it interprets as definitions of constants. If it sees a list, as above, it interprets them as names of constants to import or provide default values for.

Devel::Constants

Devel::Constants provides a reverse-mapping from constant value to name, for constants defined with the constant pragma. The simplest use:

use Devel::Constants 'to_name';

use constant DRAFT     => 1;
use constant PUBLISHED => 2;
use constant RETIRED   => 3;

my $state = DRAFT;

print "state = $state (", to_name($state), ")\n";

Obviously this can cause problems if you've more than one constant with the same value - it will return the latest constant name.

Devel::Constants also provides a function for reverse-mapping values where you have constants for bit-flags:

use Devel::Constants 'flag_to_names';

use constant OPT_A  => 0x01;
use constant OPT_B  => 0x02;
use constant OPT_C  => 0x04;
use constant OPT_D  => 0x08;

$value = OPT_B | OPT_D;

print "flags = ", join(' | ', flag_to_names($value)), "\n";

which produces the following output:

flags = OPT_D | OPT_B

By default it only intercepts constants in the current package, but you can chage that by specifying a package, before you use the relevant module. From the pod:

use Devel::Constants package => NetPacket::TCP;
use NetPacket::TCP;

When you do this it only intercepts constants in the specified package — it doesn't intercept them in the current package.

This module was written by chromatic, but you'll see me listed as maintainer in the table above. Chromatic gave me co-maint so I could update the documentation with pointers to this article, and Constant::Generate.

enum

enum is a module for defining sets of function constants with sequential values, similar to the enum type in C. The basic use is similar to other modules listed here:

use enum qw(SUN MON TUE WED THU FRI SAT);

By default the first constant will have the value 0 (zero), the next 1, and so on. You can change the start value, or even every value:

use enum qw(SUN=1 MON TUE WED THU FRI SAT);

If you want all your constants to have a prefix, you can specify this before listing the constants:

use enum qw(:OPT_ A B C);

Which defines constants OPT_A, OPT_B, and OPT_C.

You can also define bit flag constants, using the BITMASK: directive. This can be combined with a prefix:

use enum qw(BITMASK:FLAG_ X Y Z);

FLAG_X will have value 1, FLAG_Y will be 2, and FLAG_Z will be 4.

You can combine the above options and define all your constants in one invocation.

use enum qw(
            :DAY_         SUN=0 MON TUE WED THU FRI SAT
            BITMASK:FLAG_ A..C
            ENUM:MONTH_   JAN=1 FEB MAR APR MAY JUN JUL AUG SEP OCT NOV DEC
           );

Note the .. notation, which lets you define a sequence of constant names. And having used the BITMASK: directive, you can use the ENUM: directive to switch back to usual definition of enumeration constants.

enum::fields

enum::fields is used to define sequences of integer constants, similar to the enum module, but with a twist. It was created to be used in classes that are implemented as a blessed array reference, defining constants that are used to access attributes in the referenced array. And beyond that, it supports subclassing by supporting the extension of the sequence by a subclass.

Let's say you have a class for a 2D point, you might start this with:

package Point2D;

use enum::fields qw(COORD_X COORD_Y);

This would define constant COORD_X with value 0, and COORD_Y with value 1. You would access fields in an instance:

$self->[COORD_X]
$self->[COORD_Y]

You then want to subclass this to create Point3D:

package Point3D;

use parent 'Point2D';
use enum::fields::extending Point2D => qw(COORD_Z);

This defines COORD_Z as 2, i.e. the next index in the sequence.

ex::constant::vars

ex::constant::vars uses tie to provide a mechanism for creating read-only variables. It provides three different types of interface.

The first is an explicit tie interface:

use ex::constant::vars;

tie my $greeting, 'ex::constant::vars', 'hello';
tie my @cyan,     'ex::constant::vars', (0, 255, 255);
tie my %meaning,  'ex::constant::vars', (felix => 'happy', erin => 'Irish');

print "Greeting    : $greeting\n";
print "Cyan        : @cyan\n";
print "Felix means : ", $meaning{felix}, "\n";

The second interface uses the const function:

use ex::constant::vars 'const';

const SCALAR my $pause_id, 'NEILB';
const ARRAY  my @stooges,  qw(Larry Moe Curly);
const HASH   my %point,    (x => 1, y => 3);

print "PAUSE id : $pause_id\n";
print "Stooges  : @stooges\n";
print "Point    : ($point{x},$point{y})\n";

This seemed like a slightly strange notation, so I had a look at the code. SCALAR, ARRAY and HASH are defined thusly:

  sub SCALAR (\$$) { 'scalar', @_ }
  sub ARRAY  (\@@) { 'array',  @_ }
  sub HASH   (\%%) { 'hash',   @_ }

These functions are used to identify the constant type required, which is then passed to the const function, which in turn uses the tie interface under the hood.

The third way to define constants with this module is at compile time, i.e. when use'ing the module:

use ex::constant::vars (
    '$greeting'  => 'hello',
    '@cyan'      => [0, 255, 255],
    '%meaning'   => { felix => 'happy', erin => 'Irish' },
);

print "Greeting    : $greeting\n";
print "Cyan        : @cyan\n";
print "Felix means : ", $meaning{felix}, "\n";

Under the hood the const and import interfaces are using the tie interface.

This module had a couple of bugs, and would generate warnings with modern perls, so I have taken over maintenance and released a new version which addresses all known issues.

Lexical::Import

Lexical::Import is used to import things from other packages into the invoker's lexical namespace. You could use this to define an immutable variable at compile time, which could be used for constant folding / conditional compilation.

Let's say you want to define a $DEBUG constant, to use in the conditional compilation idiom. You could define the constant in a module as follows:

package LIConstants;
*LI_DEBUG = \0;
use parent 'Exporter';
our @EXPORT = qw($LI_DEBUG);
1;

Ok, that's not a very user-friendly way to define a constant. Here's some example code which shows use of the module:

use Lexical::Import 'LIConstants';
warn '1';
warn '2' if $LI_DEBUG;
warn '3';

When you run that, you get the following, as you'd hopefully expect:

1 at article/lexical-import.pl line 5.
3 at article/lexical-import.pl line 7.

You can run the following, to illustrate that it really is a compile-time constant:

% perl -MO=Deparse lexical-import.pl
lexical-import.pl syntax OK
use Lexical::Import ('LIConstants');
BEGIN {
    $^H{'Lexical::Var/$LI_DEBUG'} = 'SCALAR(0x7ffa229245e8)';
}
warn '1';
'???';
;
warn '3';

Note the ??? instead of warn '2'. I picked up that trick from Denis Evdokimov in the documentation for constant::our.

Lexical::Var

Lexical::Var can be used to export a constant to the caller's lexical environment, rather than to the caller's package. If the variable is defined as a reference to a literal value, you end up creating a compile-time lexical constant.

You could define a constant in a module as follows:

package LVConstants;
use Lexical::Var '$LV_DEBUG' => \0;
sub import {
    require Lexical::Var;
    Lexical::Var->import('$LV_DEBUG' => \$LV_DEBUG);
}
1;

Here's some example code which shows use of the module:

use LVConstants;
warn '1';
warn '2' if $LV_DEBUG;
warn '3';

When you run that, you get the following, as you'd hopefully expect:

1 at article/lexical-var.pl line 5.
3 at article/lexical-var.pl line 7.

You can run the following, to illustrate that it really is a compile-time constant:

% perl -MO=Deparse lexical-var.pl
lexical-var.pl syntax OK
use LVConstants;
BEGIN {
    $^H{'Lexical::Var/$LV_DEBUG'} = 'SCALAR(0x7fbdcc125f88)';
}
warn '1';
'???';
;
warn '3';

Readonly

Readonly helps you create read-only scalar, array and hash variables. It supports a number of ways to declare read-only variables, some of which depend on your version of Perl. The simplest interface uses a class for each type of variable:

use Readonly;

Readonly::Scalar my $scalar => 42;
Readonly::Array  my @array  => qw(red green blue);
Readonly::Hash   my %hash   => (food => 'sushi', drink => 'sake');

Readonly::Scalar our $string => 'xyzzy';
Readonly::Array  our @list   => (17, 23);
Readonly::Hash   our %map    => (x => 1, y => 2, z => 3);

There's a more generic interface, which only works in Perl 5.8 and later:

use Readonly;

Readonly my $scalar => 42;
Readonly my @array  => qw(red green blue);
Readonly my %hash   => (food => 'sushi', drink => 'sake');

Readonly our $string => 'xyzzy';
Readonly our @list   => (17, 23);
Readonly our %map    => (x => 1, y => 2, z => 3);

Under the hood Readonly uses tied variables, in the same way ex::constant::vars does. But unlike the latter, it doesn't let you use the tie interface directly. This makes it slower than most of the modules here, unless you use Readonly::XS (see below).

This is the most widely used module of those listed here, using the measure of how many other CPAN distributions have one of these modules as a pre-requisite. It also has the most open issues in RT.

Readonly::XS

Readonly::XS is a companion module for Readonly. If you have it installed, Readonly will use it for any scalar constants you declare. It creates a constant by setting the SvREADONLY flag on the variable. At the moment there's a bug which means that you only get the performance benefit if you use Readonly::Scalar rather than Readonly.

You don't use this module, you just need to have it installed.

Scalar::Constant

Scalar::Constant says it's for "lightweight constant scalars":

use Scalar::Constant ANSWER   => 42;
use Scalar::Constant GREETING => 'Hej';

print "scalar   = $ANSWER\n";
print "greeting = $GREETING\n";

Internally when you define a constant, it builds up a string which it passes to eval (the dangerous version). For the above constant, the eval string is:

*main::ANSWER   = \ 42;
*main::GREETING = \ 'Hej';

Note that because of the mechanism used, you cannot use this module to create a constant with an arrayref or hashref value.

If you use this module, make sure you use version 0.001003 or later: earlier versions would fail if you tried to declare a scalar constant with a string value.

Scalar::Construct

Scalar::Construct provides a number of functions for constructing scalar objects. The one of interest here is the function constant:

use Scalar::Construct qw(constant);

$ref = constant($value);

This creates an immutable scalar initialised to the value passed, and returns a reference to it. But how do you create a constant that you can use in your code? It's not obvious from the documentation.

If you want to define constants in a module that is then used by the rest of your code, you can define your module as follows:

package SCConstants;
use Scalar::Construct qw(constant);

my $SC_DEBUG = constant(0);

sub import {
    require Lexical::Var;
    Lexical::Var->import('$SC_DEBUG' => $SC_DEBUG);
}

1;

Here's some example code which shows use of the module:

use SCConstants;
warn '1';
warn '2' if $SC_DEBUG;
warn '3';

When you run that, you get the following, as you'd hopefully expect:

1 at article/scalar-construct.pl line 5.
3 at article/scalar-construct.pl line 7.

You could also use it in conjunction with Lexical::Import — the example module in the Lexical::Import could be lifted, with just the glob assignment changed:

*DEBUG = constant(0);

Given both examples given here work with other modules which can be used on their own, why would you use Scalar::Construct? The following is taken from the Scalar::Construct documentation:

If VALUE is actually a compile-time constant that can be expressed as a literal, such as 123, it would appear that a reference to a constant object with that value can be created by a Perl expression such as \123. However, Perl has some bugs relating to compile-time constants that prevent this working as intended. On Perls built for threading (even if threading is not actually used), such a scalar will be copied at surprising times, losing both its object identity and its immutability. The function supplied by this module avoids these problems.

Scalar::Readonly

Scalar::Readonly is another module which basically is a layer on top of the SvREADONLY internal function (used by Const::Fast). The readonly_on() function is used to make a scalar variable read-only:

use Scalar::Readonly ':all';

readonly_on(my $my_int    = 42);
readonly_on(my $my_string = 'hello');

readonly_on(our $our_int    = 15);
readonly_on(our $our_string = 'goodbye');

The module also provides a readonly_off() function, and readonly(), which tells you whether a scalar is read-only or not:

use Scalar::Readonly ':all';

my $scalar;

$scalar = 3;

print "scalar = $scalar\n";
print "first, readonly(scalar) = ", readonly($scalar), "\n";

readonly_on($scalar);
print "then, readonly(scalar) = ", readonly($scalar), "\n";

eval { $scalar++ };
print "nope, can't change scalar ($scalar)\n" if $@;

readonly_off($scalar);
print "finally, readonly(scalar) = ", readonly($scalar), "\n";

eval { $scalar++ };
print "and at the end, \$scalar=$scalar\n";

When you run the above you'll get:

scalar = 3
first, readonly(scalar) = 0
then, readonly(scalar) = 1
nope, can't change scalar (3)
finally, readonly(scalar) = 0
and at the end, $scalar=4

Comparison

There are two basic types of constant module: functions and immutable variables. Both approaches have strengths and weaknesses. Some of the points below relate to current modules, rather than being inherent in the particular approach.

Function constants:

Immutable variables:

The following is a map to help you identify the modules to consider, based on your needs:

The following tables show the results of simple performance comparisons for each type of constant. I defined constants with each module, and then accessed the constant one hundred million times.

Scalar
ModuleTime (s)
enum::fields3.1
constant3.2
Constant::Generate3.5
enum3.6
constant::def3.6
Lexical::Import3.7
Constant::FromGlobal3.8
Scalar::Constant4.3
Scalar::Construct4.5
Lexical::Var4.5
Readonly::Scalar4.8
constant::our5.0
constant::lexical5.2
Attribute::Constant5.2
Scalar::Readonly5.7
Const::Fast6.0
Config::Constants6.6
constant::defer11.5
ex::constant::vars94.2
Readonly112.1
Array/List
ModuleTime (s)
Const::Fast4.9
Attribute::Constant5.1
constant::def41.4
constant41.9
constant::defer42.1
constant::lexical43.5
ex::constant::vars147.8
Readonly164.6
Hash
ModuleTime (s)
Attribute::Constant7.2
Const::Fast8.0
ex::constant::vars200.5
Readonly221.4

Notice that Readonly and Readonly::Scalar are listed. To get the performance benefits of Readonly::XS you currently have to use Readonly::Scalar and not Readonly.

Conclusion

As always seems to be the case, there is no single module which will meet all of your needs.

In the previous version of this review, I wrote:

My perfect scenario doesn't exist: I'd like a module which can be used to declare immutable variable style constants at compile time, and for those variables to work in a conditional compilation idiom. ex::constant::vars covers the first part, but it's very slow. I'll have to ask p5p whether the second part is possible, or even makes sense.

I asked p5p, and as a result learned about Lexical::Var and Lexical::Import. Those modules provide everything I wanted, but they're not really user-friendly / elegant. Now I want a module that can be used to elegantly declare immutable variables at compile time, so they can be used in constant folding.

For the app which started this, I've switched from Readonly to Const::Fast.

comments powered by Disqus