Previous Page
Next Page

11.3. Symbolic References

Never use symbolic references.

If use strict 'refs' isn't in effect, a string containing the name of a variable can be used to access that variable:

    my $hash_name = 'tag';

    ${$hash_name}{nick}   = ${nick};
    ${$hash_name}{rank}   = ${'rank'}[-1];     # Most recent rank
    ${$hash_name}{serial} = ${'serial_num'};

You can even use the arrow notation on a plain string to get the same effect:

    my $hash_name = 'tag';

    $hash_name->{nick}   = ${nick};
    $hash_name->{rank}   = 'rank'->[-1];
    $hash_name->{serial} = ${'serial_num'};

A string used in this way is known as a symbolic reference. It's called that because when Perl encounters a string where it was expecting a reference, it uses the string to look up the local symbol table and find an entry for the relevant variable of the same name.

Hence the previous examples (assuming they are in package main) are both equivalent to:

    (*{$main::{$hash_name}}{HASH})->{nick}   = ${*{$main::{'nick'}}{SCALAR}};
    (*{$main::{$hash_name}}{HASH})->{rank}   = *{$main::{'rank'}}{ARRAY}->[-1];
    (*{$main::{$hash_name}}{HASH})->{serial} = ${*{$main::{'serial_num'}}{SCALAR}};

(For the viewers at home, the breakdown of that first line is shown in Figure 11-1. "Breakdown" being the operative word here.)

Figure 11-1. Symbolic reference breakdown

You'd never willingly write complex, unreadable code like that. So don't write code that's surreptitiously equivalent to it.

The example deconstruction illustrates that a symbolic reference looks up the name of a variable in the current package's symbol table. That means that a symbol reference can only ever refer to a package variable. And since you won't be using package variables in your own development (see Chapter 5), that will only lead to confusion. For example:

    # Create help texts...
    Readonly my $HELP_CD  => 'change directory';
    Readonly my $HELP_LS  => 'list directory';
    Readonly my $HELP_RM  => 'delete file';
    Readonly my $NO_HELP  => 'No help available';

    # Request and read in next topic...
    while (my $topic = prompt 'help> ') {
        # Prepend "HELP_", find the corresponding variable (symbolically),
        # and display the help text it contains...
        if (defined ${"HELP_\U$topic"}) {
            print ${"HELP_\U$topic"}, "\n";
        # Otherwise, display an unhelpful message...
        else {
            print "$NO_HELP\n";

The ${"HELP_\U$topic"} variable interpolates the requested topic ($topic) into a string, capitalizing the topic as it does so (\U$topic). It then uses the resulting string as the name of a variable and looks up the variable in the current symbol table.

Unfortunately, the desired help text won't ever be in the current symbol table; all the help texts were assigned to lexical variables, which don't live in symbol table entries.

The use of symbolic references almost always indicates a misdesign of the program's data structures. Rather than package variables located via symbolic references, what is almost always needed is a simple, lexical hash:


    # Create table of help texts and default text...
Readonly my %HELP => ( CD => 'change directory', LS => 'list directory', RM => 'delete file', ); Readonly my $NO_HELP => 'No help available';
# Request and read in next topic...
while (my $topic = prompt 'help> ') {
# Look up requested topic in help table and display it...
if (exists $HELP{uc $topic}) { print $HELP{uc $topic}, "\n"; }
# Otherwise, be helpless...
else { print "$NO_HELP\n"; } }

    Previous Page
    Next Page