Recipe 1.21 Constant Variables

1.21.1 Problem

You want a variable whose value cannot be modified once set.

1.21.2 Solution

If you don't need it to be a scalar variable that can interpolate, the use constant pragma will work:

use constant AVOGADRO => 6.02252e23;

printf "You need %g of those for guac\n", AVOGADRO;

If it does have to be a variable, assign to the typeglob a reference to a literal string or number, then use the scalar variable:

*AVOGADRO = \6.02252e23;
print "You need $AVOGADRO of those for guac\n";

But the most foolproof way is via a small tie class whose STORE method raises an exception:

package Tie::Constvar;
use Carp;
    my ($class, $initval) = @_;
    my $var = $initval;
    return bless \$var => $class;
sub FETCH {
    my $selfref = shift;
    return $$selfref;
sub STORE {
    confess "Meddle not with the constants of the universe";

1.21.3 Discussion

The use constant pragma is the easiest to use, but has a few drawbacks. The biggest one is that it doesn't give you a variable that you can expand in double-quoted strings. Another is that it isn't scoped; it puts a subroutine of that name into the package namespace.

The way the pragma really works is to create a subroutine of that name that takes no arguments and always returns the same value (or values if a list is provided). That means it goes into the current package's namespace and isn't scoped. You could do the same thing yourself this way:

sub AVOGADRO( ) { 6.02252e23 }

If you wanted it scoped to the current block, you could make a temporary subroutine by assigning an anonymous subroutine to the typeglob of that name:

use subs qw(AVOGADRO);
local *AVOGADRO = sub ( ) { 6.02252e23 };

But that's pretty magical, so you should comment the code if you don't plan to use the pragma.

If instead of assigning to the typeglob a reference to a subroutine, you assign to it a reference to a constant scalar, then you'll be able to use the variable of that name. That's the second technique given in the Solution. Its disadvantage is that typeglobs are available only for package variables, not for lexicals created via my. Under the recommended use strict pragma, an undeclared package variable will get you into trouble, too, but you can declare the variable using our:

local *AVOGADRO = \6.02252e23;

The third solution provided, that of creating your own little tie class, might appear the most complicated, but it provides the most flexibility. Plus you get to declare it as a lexical if you want.

tie my $AVOGADRO, Tie::Constvar, 6.02252e23;

After which this is okay:

print "You need $AVOGADRO of those for guac\n";

But this will get you in trouble:

$AVOGADRO = 6.6256e-34;   # sorry, Max

1.21.4 See Also

Recipe 1.15; Recipe 5.3; the discussion on folding constant subroutines toward the end of the section on "Compiling Your Code" in Chapter 18 of Programming Perl; the CPAN module Tie::Scalar::RestrictUpdates might give you some other ideas