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

I wonder if anyone can help with a problem I'm having with the constant pragma. It has almost driven me to distraction this afternoon...

The following code snippet illustrates the problem. I define two constants near the top and then use them in two different hash definitions. The first one comes out via Data::Dumper as I would expect but in the second one the constants are not converted to the values I want and remain as the constant labels.

use strict; use warnings; use Data::Dumper; use constant E_MESYSTEM => 1; use constant E_MEENTITY => 2; my %element_type = ( system => E_MESYSTEM, entity => E_MEENTITY, ); my %generator_lookup = ( E_MESYSTEM => 'G_SYSTEM_SYSTEM_ID', E_MEENTITY => 'G_ENTITY_ENTITY_ID', ); print Dumper(\%element_type); print Dumper(\%generator_lookup); __END__ #Ouput correct $VAR1 = { 'system' => 1, 'entity' => 2 }; #Output incorrect $VAR1 = { 'E_MESYSTEM' => 'G_SYSTEM_SYSTEM_ID', 'E_MEENTITY' => 'G_ENTITY_ENTITY_ID' };

Regards,
Dom.

Replies are listed 'Best First'.
Re: Problem with constant pragma and some hash definitions
by sauoq (Abbot) on Jul 18, 2003 at 18:36 UTC

    Constants declared with use constant are problematic when used as hash keys. Constants are actually implemented as functions. In hash lookups and on the left hand side of a fat comma ('=>'), barewords are interpretted as strings rather than function calls. You can get the effect you want by prefixing them with a '&' and forcing them to be called as functions like this:

    my %generator_lookup = ( &E_MESYSTEM => 'G_SYSTEM_SYSTEM_ID', &E_MEENTITY => 'G_ENTITY_ENTITY_ID', );

    -sauoq
    "My two cents aren't worth a dime.";
    
      Another way around this is
      my %generator_lookup = ( E_MESYSTEM , 'G_SYSTEM_SYSTEM_ID', E_MEENTITY , 'G_ENTITY_ENTITY_ID', );
      The comma operator does not quote the lefthand side like => does. Just as a side note, I did notice that vim correctly color coded the source when I cut and pasted your code. The constants where correctly colored as quoted strings when I pasted them in, when I changed the => to , the E_MESYSTEM and E_MEENTITY changed color indicating they where not quoted strings. That is very useful.

      --

      flounder

        Avoiding the fat comma doesn't help in the case of hash lookups though. And, since you have to write it as $generator_lookup{&E_MESYSTEM} when looking it up, you might as well put the ampersand in when initializing it too, even if only for the sake of consistency.

        -sauoq
        "My two cents aren't worth a dime.";
        

      Many thanks sauoq for your rapid answer. Just one follow up question if I may; is there any downside in doing this? In the real bit of code there will be 30-40 values at a maximum.

      Regards,
      Dom.

        Actually there is. As soon as you add that ampersand the constants cease to be constants. And instead of inlining the value during compile-time, Perl actually calls the functions each time. Therefore I'd rather add an empty pair of braces:

        %hash = ( FOO() => 'FOO description, ...)
        Try to run these three and see the difference:
        perl -MO=Deparse -e "use constant FOO => 5; print FOO,qq{\n};" perl -MO=Deparse -e "use constant FOO => 5; print &FOO,qq{\n};" perl -MO=Deparse -e "use constant FOO => 5; print FOO(),qq{\n};"

        Jenda
        Always code as if the guy who ends up maintaining your code will be a violent psychopath who knows where you live.
           -- Rick Osborne

        Edit by castaway: Closed small tag in signature

        Just one follow up question if I may; is there any downside in doing this?

        A downside to using the ampersand notation? Not except for needing to keep track of where you need one and where you don't. (I actually think of that as a downside to using constant.)

        Update: "Actually, there is." Jenda is right.

        -sauoq
        "My two cents aren't worth a dime.";
        
      instead of prefixing with ampersand, you can force them as a sub call: "E_MESYSTEM()" etc.
        instead of prefixing with ampersand, you can force them as a sub call

        Actually, prefixing them with an ampersand does force a sub call and, as Jenda pointed out elsewhere in this thread, that isn't always desirable. Using parens, as you suggest, leaves the constants as candidates for inlining. When they are inlined, they aren't sub calls at all, but substituted at compile time with their value. That's a good thing precisely because it saves you that call overhead.

        -sauoq
        "My two cents aren't worth a dime.";
        
Re: Problem with constant pragma and some hash definitions
by Enlil (Parson) on Jul 18, 2003 at 18:41 UTC
Re: Problem with constant pragma and some hash definitions
by perrin (Chancellor) on Jul 18, 2003 at 18:44 UTC
    You can't use constants in a quoted context (like the left side of a => operator) without calling them as subroutines. This is why I advise people to avoid the use of the constant pragma. Just use globals instead.
      Don't use globals. If you have a problem with constants, I would use a singleton or a globals hash. Single globals become a serious problem.

      ------
      We are the carpenters and bricklayers of the Information Age.

      Don't go borrowing trouble. For programmers, this means Worry only about what you need to implement.

      Please remember that I'm crufty and crochety. All opinions are purely mine and all code is untested, unless otherwise specified.

        Globals aren't that bad. They are package scoped afterall, and very efficient too. If you use a convention, like consistently using uppercase for constants, you shouldn't have many problems. If you want to keep the constants private, you can use file scoped lexicals instead of globals and protect yourself even further.

        A global hash isn't a terrible alternative, but the syntax is messy and you don't benefit from strict checking.

        But a singleton? Ugh...

        How about a tied scalar? (just kidding)

        -sauoq
        "My two cents aren't worth a dime.";
        
        I don't see a problem with using globals for constant values. They have the same scope as the subroutines created by the constant pragma.