enoch has asked for the wisdom of the Perl Monks concerning the following question:

I am rewriting some of my old one-liners that grew into oft-used utilities. Whilst doing so, I wanted to move a lot of hard-coded strings (pathnames and such) into constants at the top of the script (via the constant pragma). So, I type:
use constant CONST => { foo => 'bar', fiz => 'baz' }; foreach (keys CONST) { print "$_\n"; }
This, of course, returned the error:
Type of arg 1 to keys must be hash (not constant item)
"Owe," I thought, and I changed it to:
foreach (keys %{CONST}) { print "$_\n"; }
This resulted in the error:
Variable "%CONST" is not imported
Hmmmmmm....print CONST; printed HASH(0x1a9de10), which is what I expected. What I did not expect is that I could not access it.

Can someone please enlighten me? In the meantime, I guess I'll try various dereferencing combinations and read up on constant and perlref.


Replies are listed 'Best First'.
(ar0n) Re: Accessing Constant Hashes
by ar0n (Priest) on Jan 16, 2002 at 02:27 UTC
      If I could pick your brain, is the constant pragma implemented as a sort of psuedo-subroutine? Otherwise, why would calling the constant with an open and close paren force it to resolve to a hash?

      ++ar0n and thanks,

        As ar0n already replied, the constant pragma does create a subroutine. If you're curious, this is an inlined subroutine with a null prototype (one of the few places where a prototype is really useful). Looking in the constant module, the following code is where the constant is actually created:

        { no strict 'refs'; my $full_name = "${pkg}::$name"; $declared{$full_name}++; if (@_ == 1) { my $scalar = $_[0]; *$full_name = sub () { $scalar }; } elsif (@_) { my @list = @_; *$full_name = sub () { @list }; } else { *$full_name = sub () { }; } }

        What the null prototype does is allow your constant to behave like a Perl built-in:

        use constant FOO => 7; print FOO + 1;

        That prints 8, just like you would expect. Contrast that to this:

        sub FOO {7}; print FOO + 1'

        That will print 7. Why? Because Perl will interpret the +1 as being an argument to &FOO, which is silently discarded as it is not used. Yuck! With the null prototype, Perl knows that nothing coming after FOO can be an argument, so everything parses as you expect. It's also nice to note, from the docs, that as of Perl 5.004, Perl will replace all instances of your constant with the returned value, thus saving you the overhead of a subroutine call.


        Join the Perlmonks Setiathome Group or just click on the the link and check out our stats.

        It's exactly that, a subroutine.

        Calling it with a () -- or a + -- disambiguates it from %{CONST}, which is the equivalent of %CONST.

        [ ar0n -- want job (boston) ]

Re: Accessing Constant Hashes
by perrin (Chancellor) on Jan 16, 2002 at 02:36 UTC
    ar0n gave the right answer, but I just want to take a moment to say that constant in the current implementation is totally lame for exactly this reason, and I think you should avoid it completely. It doesn't work in quoted strings or HERE docs either without similar tricks. Just use globals or lexicals at the top of the file for your constants.

      I personally would recommend using a constant if there's a possibility of constant folding:

      #!/usr/local/bin/perl use O qw/ Deparse /; use constant CONDITION => 0; if( CONDITION ) { print "condition true\n"; } else { print "condition false\n"; } __END__ ## and here's the output. notice that there are ## no if's me@myhost> ./ ./ syntax OK sub CONDITION () { package constant; $scalar; } print "condition false\n";;

      Otherwise I guess I could care less, but I use it more for a mental note than anything else -- it makes me aware that this value should be a constant