Define constants for use by other modules in Perl

We regularly encounter lists of constants. Quick examples are HTTP status codes, Virtual Key Codes, lists of error codes and many many more. Regardless of the language you are using, you need a way to use those constants via friendlier symbolic names rather than literal numbers. Among other things, doing so helps with maintenance. If nothing else, it makes code more readable. Also, if, say, ECONNABORTED is 0x35 on one system and 0x53 on another system, you only need to change the definition of the symbolic name in one place instead of trying to figure out if every reference to 0x35 or 53 or even 065 etc in your code refers to that error code.

In C, people have customarily used macro definitions for this purpose. If you really want to be able to get help from the debugger in identifying these constants, you can also use a bunch of const ints or enums.

In Java, you get to have a class with a bunch of public static final variables.

In Python, there are no constants. You can name a variable using ALL_CAPS to indicate it should be treated as a constant, or you can define a class with just a bunch of getters and no setters. You can also make __setattr__ do nothing. As of version 3.4, Python also comes with an Enum class.

In Ruby, you indicate that a variable is meant to be a constant by capitalizing the first character of its name. The interpreter than emits a warning (not an error) if its value is changed. There is also a neat enum gem you can use.

These are just some examples: I don't mean for this post to be a review of all the ways you can associate symbolic names with a bunch of constants in a variety of programming languages, so I'll move on.

Python and Ruby are similar to Perl in this regard. In Perl, there is really no explicit built in language syntax for declaring variables to be constant. The $ALL_CAPS convention can be found in various modules. E.g.:

$ perl -MSocket=:crlf -e 'print $Socket::CRLF' | xxd
00000000: 0d0a                                     ..
$ perl -MSocket=:crlf -e '$CRLF = "\x23"; print $CRLF' | xxd
00000000: 23

At least, in Ruby you get a warning.

Perl also has constant functions. When a function has an empty prototype, and if its return value after optimization and constant folding is either a constant or a lexical scalar with no other references, then the return value of the function is inlined at compile time. That is, if you have:

sub number() { 42 }

# ...

my $number = number;

then no subroutine invocation occurs at run time, and the assignment is compiled as my $ns = 42 as can be seen in this simple example:

$ perl -MO=Deparse -e 'sub number() { 42 }; my $n = number'
sub number () {
    42;
}
my $n = 42;
-e syntax OK

The constant pragma provides sugar for defining constant functions:

use constant NUMBER => 42;

my $number = NUMBER;

So, if you had a whole bunch of constants you wanted to collect in a module, you could, for example, write:

package Win32::VK;

use Exporter qw( import );

our @EXPORT = ();

our %EXPORT_TAGS = (
    'all' => [ qw( VK_LBUTTON VK_RBUTTON VK_CANCEL VK_MBUTTON ) ],
    # etc
);

our @EXPORT_OK = (
    @{ $EXPORT_TAGS{all} },
    # etc
);

use constant VK_LBUTTON => 0x01;
use constant VK_RBUTTON => 0x02;
use constant VK_CANCEL  => 0x03;
use constant VK_MBUTTON => 0x04;

# etc

Incidentally, Win32::GuiTest currently almost does this, but not quite. By not using an empty prototype, the author gives up on the compile time optimization.

In the example above, I used Exporter's import so that I could, in a different module, do:

use Win32::VK qw( VK_LBUTTON );

# ...

if ($vk == VK_LBUTTON ) {
    # ...
}
elsif ( $vk == Win32::VK::VK_RBUTTON ) {
    # ...
}

This is all well and good until you want to interpolate one of the constants into a string which necessitates either something like

print "The code is @{[ VK_LBUTTON ]}\n"

or

sprintf "The code is %d\n", VK_LBUTTON;

Also, you have to do a bit of mental gymnastics every time you encounter such constants used as hash keys. Quick! What do you get when you write

my %h = ( VK_LBUTTON => 'left button' );

Well, it depends. Not to mention the gymnastics one needs to write code to list all the constants and their values defined in a given namespace.

This brings me to my long time favorite module for defining constants: Const::Fast.

Before we get into that, let me digress a bit and mention Importer. I must admit, at first I did not quite get Chad's arguments in support of this module, but, after using Importer a few times, I am sold. With Importer, the example Win32::VK module above would no longer need to import Exporter's import method to allow other modules to import stuff from it ;-). Instead, the module would just contain the necessary definitions without the use Exporter qw( import ). The using module would import what it wants using Importer:

use Importer 'Win32::VK' => qw( VK_LBUTTON );

I find this much cleaner. This way, the code using the constants supplies the machinery to import them, and the code defining the constants need not concern itself with it: It just needs to list what it makes available.

Coming back to Const::Fast ... I first found out about it thanks to Neil Bowers's excellent review of CPAN modules that can be used to define or work with constants. In the benchmarks, Const::Fast wasn't necessarily the fastest, but it was fast enough and it had friendly syntax. In addition to allowing the definition of simple scalar constants, Const::Fast's syntax allows costant rich data structures to be defined. For example, the following code, despite appearances, does not define a reference to constant hash:

use constant ERROR => {
    FILE_NOT_FOUND => 42,
};

ERROR->{FILE_NOT_FOUND} = 1; # oops

Also, with constant.pm, the "constants" are always in package scope by virtue of the fact that they are subroutines in disguise. This may or may not matter for you, but it does make defining static class constants that cannot be accessed from the outside impossible.

So, here is how I like defining my constants. First, I like to put everything in a single module, say, My::FancyModule::Constants. Then, I like grouping them into hashes. For example, going back to the various Win32 constants defined in Win32::GuiTest, here's what I would have done if I were writing that module from scratch:

package Win32::GuiTest::Constants;

use strict;

our @EXPORT = ();
our @EXPORT_OK = qw( %VK %SW ... );

const our %VK => (
    # ...
    SHIFT => 0x10,
    CONTROL => 0x11,
    MENU => 0x12,
    PAUSE => 0x13,
    CAPITAL => 0x14,
    # ...
);

const our %SW => (
    # ...
    MAXIMIZE => 3,
    MINIMIZE => 6,
    NORMAL => 1,
    RESTORE => 9,
    # ...
)
# ...

Then, one could use those constants by simply doing:

use Importer 'Win32::GuiTest::Constants' => qw( %VK );

print "The key code is $VK{SHIFT}\n";

If I want to read the constants from a configuration file, that's trivial. If I want to export them to JSON, YAML, INI, or whatever else, that's also trivial. I can interpolate them willy-nilly.

For those who have taken seriously Exporter's stance against exporting variables, this takes some getting used to. Keep in mind that the admonition is there to make sure you don't write code that willy nilly modifies global variables. However, in this case, the variables we are exporting are not modifiable. Maybe you can convince yourself that the concern really does not apply in this instance. If you try to refer to a non-existent constant, you get an error (albeit during run time) as shown below. The code is snipped for brevity, I just copied and pasted all the constants from HTTP::Status documentation:

use Const::Fast;

const my %HTTP_STATUS => map /^HTTP_(\S+)\s+\((\S+)\)$/mg, <<HTTP_STATUS;
HTTP_CONTINUE                        (100)
HTTP_SWITCHING_PROTOCOLS             (101)
HTTP_PROCESSING                      (102)

HTTP_OK                              (200)
HTTP_CREATED                         (201)
...
HTTP_STATUS

eval { print "$HTTP_STATUS{DOES_NOT_EXIST}\n" } or print "[email protected]\n";
eval { $HTTP_STATUS{GONE} = 999 } or print "[email protected]\n";

Output:

Attempt to access disallowed key 'DOES_NOT_EXIST' in a restricted hash at t.pl line 72.

Modification of a read-only value attempted at t.pl line 73.

There is, of course, a speed penalty for the convenience. It ranges from almost identical performance to a few percent in favor of constant.pm to constant.pm being about twice as fast, depending the exact method of measurement. I haven't felt the need to study performance as Neil did, as Const::Fast is still fast enough. When I hit a performance issue, the real culprit usually was somewhere else.

Of course, constant.pm is still the right tool to use for debug guards etc. That is, if you have:

use constant USE_LOGGING => 0;

#
# ...
#

expensive_logging_call($context) if USE_LOGGING;

the whole conditional will be eliminated at compile time, ensuring zero overhead from logging code when it is disabled.

For all other uses, I have found Const::Fast to be the most convenient way of translating a bunch of constants to Perl. By using Importer, these modules can consist only of the constant definitions and export declarations, and nothing else.

Neat.

PS: You can discuss this post on r/perl.

PPS: This post expands on my answer to How can I define constants in a separate file in Perl? on Stackoverflow.