There is no Title

It's a blog.

0 notes

Declare exportable constants in a module with Const::Exporter

Most non-trivial applications or libraries will have a set of often-used constants in their code. We’ll take it for granted that the developers are defining a set of shared constant names.

Perl is no exception, and there are several modules for defining constants.

Constants can be exported like any other symbol. Simply define them as normal and add them to the @EXPORT/@EXPPORT_OK/%EXPORT_TAGS variables and use your favorite export module.  Which is fine when you only have a few constants.

But sometimes one has many constants that are used throughout the application/library, and are useful for other applications.  So it makes sense to have a set of shared modules that define them.  Manuall maintaining a module with dozens of constants is error prone.

There are existing modules on CPAN to export constants, but don’t suit my needs:

  • Declare (interpolable) scalar read-only values as well as arrays and hashes.
  • Declare them in export tags, with the ability to declare constants in multiple tags.
  • Declare relationships between constants, e.g. that constant $Y = $X + 1 or that @FOO = ($X, $Y, $Z).
  • Declare constants whose values are the results of a computation (such as reading a file or looking at the system state, etc.).
  • Declare constants in separate blocks, so that their declarations can be more easily interspersed with POD.

The existing modules do some of these things, but not all of them. As far as I am aware, none of them support interpolatable constants. My fallback was to use declare constants using something like Readonly or Const::Fast and automatically set up exports.

To simplify this, I wrote Const::Exporter. It does the above things, and a few more (such as creating enumerated values, akin to Readonly::Enum). Basically, you define constants like so:

package MyApp::Constants;
use strict;
use warnings;

use Const::Exporter
   tag_a => [                  # use MyApp::Constants /:tag_a/;
      'foo'  => 1,             # exports "foo"
      '$bar' => 2,             # exports "$bar"
      '@baz' => [qw/ a b c /], # exports "@baz"
      '%bo'  => { a => 1 },    # exports "%bo"
   tag_b => [                  # use MyApp::Constants /:tag_b/;
      'foo',                   # exports "foo" (same as from ":tag_a")

# `use Const::Exporter` can be specified multiple times
use Const::Exporter
   tag_b => [                 # we can add symbols to ":tab_b"
      'moo' => $bar,          # exports "moo" (same value as "$bar")
   default => [qw/ fo $bar /]; # exported by default  

and then import your constants as needed:

package MyApp;

use MyApp::Constants qw/ :all /;

The way Const::Exporter works is that every time you “use” the module, it processes the arguments and ensures that

  • The @EXPORT, @EXPORT_OK, %EXPORT_TAGS variables are exported to the caller;
  • &Exporter::import is exported to the caller, if there is no import method already defined;
  • The symbols specified in the arguments are exported to the caller, as constants, and added to the appropriate tags.

In the background, it uses Const::Fast to ensure variables are read-only, and Package::Stash to do the exporting.

This module is also on GitHub. Patches and comments are welcome.

Filed under perl constants