Документация
HTML CSS PHP PERL другое

Section 4.5.  Constants

 
Previous
Table of Contents
Next

4.5. Constants

Use named constants, but don't use constant.

Raw numbers that suddenly appear in the middle of a program are often mysterious, frequently confusing, and always a potential source of errors. Certain types of unprintable character stringsfor example, initialization strings for modemsare similarly awkward.

A line like this:

    print $count * 42;

is unsatisfactory, as the reader may have no idea from the context why the variable is being multiplied by that particular number. Is it 42: the number of dots on a pair of dice? Or 42: the decimal ASCII value of asterisk? Or 42: the number of chromosomes in common wheat? Or 42: the angular spread of a rainbow? Or 42: the number of lines per page in the Gutenberg Bible? Or 42: the number of gallons per barrel of oil?

Replace these kinds of raw literals with a read-only lexical variable whose name explains the meaning of the number:


    use Readonly;
    Readonly my $MOLYBDENUM_ATOMIC_NUMBER => 42;

    
# and later...
print $count * $MOLYBDENUM_ATOMIC_NUMBER;

The Readonly CPAN module exports a single subroutine (Readonly( )) that expects two arguments: a scalar, array, or hash variable, and a value. The value is assigned to the variable, and then the variable's "read-only" flag is set, to prevent any further assignments. Note the use of all-uppercase in the variable name (in accordance with the guideline in Chapter 3) and the use of the fat comma (because the constant name and its value form a natural pairsee "Fat Commas" later in this chapter).

If you accidentally try to assign a new value to a constant:

    $MOLYBDENUM_ATOMIC_NUMBER = $CARBON_ATOMIC_NUMBER * $NITROGEN_ATOMIC_NUMBER;

the interpreter immediately throws an exception:


    Modification of a read-only value attempted at nuclear_lab.pl line 13

Even when the constant is instantly recognizable, and highly unlikely ever to change, it's still better to give it a name. Naming the constant improves the level of abstraction, and therefore the readability, of the resulting code:


    use Readonly;
    Readonly my $PI => 3.1415926;

    
# and later...
$area = $PI * $radius**2;

The same approach is also particularly helpful when dealing with empty strings:


    use Readonly;
    Readonly my $EMPTY_STR => q{};

    
# and later...
my $error_msg = $EMPTY_STR;

This named constant is far less likely to be overlooked or misinterpreted than a raw '' might be. It's also less mystifying to inexperienced Perl programmers than a q{}. Likewise, the other visually ambiguous literals can be made much clearer with:


    Readonly my $SPACE        => q{ };
    Readonly my $SINGLE_QUOTE => q{'};
    Readonly my $DOUBLE_QUOTE => q{"};
    Readonly my $COMMA        => q{,};

The obvious question at this point is: why use Readonly instead of use constant? After all, the constant pragma comes standard with Perl, and the constants it creates don't have those annoying sigils.

Well, it turns out those annoying sigils are actually highly useful, because they allow Readonly-generated constants to be interpolated into other strings. For example:


    use Readonly;
    Readonly my $DEAR      => 'Greetings to you,';
    Readonly my $SINCERELY => 'May Heaven guard you from all misfortune,';

    $msg = <<"END_MSG";
    $DEAR $target_name

    $scam_pitch

    $SINCERELY

    $fake_name
    END_MSG

Bareword constants can't be interpolated, so you have to write:

    use constant (
        DEAR      => 'Greetings to you,',
        SINCERELY => 'May Heaven guard you from all misfortune,',
    );

    # and later...

    $msg = DEAR . $target_name
           . "$scam_pitch\n\n"
           . SINCERELY
           . "\n\n$fake_name";

which is both harder to read and easier to get wrong (for example, there's a space missing between the DEAR and the $target_name).

The sigils also ensure that constants behave as expected in autostringifying contexts:


    use Readonly;
    Readonly my $LINES_PER_PAGE => 42;       
# Gutenberg-compatible

    # and later...
$margin{$LINES_PER_PAGE}
# sets $margin{'42'}
= $MAX_LINES - $LINES_PER_PAGE;

In contrast, constants created by use constant are treated as barewords anywhere a string is expected:

    use constant (
        LINES_PER_PAGE => 42
    );

    # and later...

    $margin{LINES_PER_PAGE}               # sets $margin{'LINES_PER_PAGE'}
        = MAX_LINES - LINES_PER_PAGE;

But perhaps most importantly, use Readonly allows you to create lexically scoped constants at runtime:


    EVENT:
    while (1) {
        use Readonly;
        Readonly my $EVENT => get_next_event( );

        last EVENT if not defined $EVENT;

        if ($VERBOSE) {
            print $EVENT->desc( ), "\n";
        }

    
# process event here...
}

whereas use constant creates package-scoped constant subroutines at compile time:

    EVENT:
    while (1) {
        use constant EVENT => get_next_event( );

        last EVENT if not defined EVENT;

        if (VERBOSE) {
            print EVENT->desc( ), "\n";
        }

        
        # process event here...
    }

That difference is critical here, because the use constant version will call get_next_event( ) only onceat compile time. If no event is available at that time, the subroutine will presumably return undef, and the loop will terminate before completing even a single iteration. The behaviour will be even worse if an event is available at compile time, in which case that event will be bound forever to the EVENT constant, and the loop will never terminate. The Readonly version doesn't suffer the same problem, because it executes at runtime, reinitializing the $EVENT constant each time the loop iterates.

Note that to get the full benefits of Readonly, you need to be using Perl 5.8 and have installed the associated Readonly::XS module, which requires precompilation. Be sure to read the module's documentation for a careful description of the pros and cons of using Readonly under earlier versions of Perl or without the precompiled helper module.

If you decide not use the Readonly module in production code (for performance or political reasons), then using constant is still better than using literal values.

    Previous
    Table of Contents
    Next
    © 2000- NIV