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

I am having some trouble with using a constant string. Specifically i have code along the following line:
use constant WISCONSIN => "WI"; use constant ILLINOIS => "IL"; ... my %capitol_map = ( WISCONSIN => "Madison", ILLINOIS => "Springfield", ... ); ... my $abbrev = "WI"; if ( exists $capitol_map{$abbrev} ) { print "capitol = $capitol_map{$abbrev}\n"; }
exists() returns false.
I had assumed that in the hash creation, that the constants would be interpolated into their text values. This is not the case though, and if i look at the hash with Data::Dumper, the keys look like WISCONSIN or ILLINOIS
I must be missing some subtle point here.
thanks

Replies are listed 'Best First'.
•Re: use constant for strings
by merlyn (Sage) on Apr 18, 2003 at 17:48 UTC
    Ahh yes, you've hit the bugaboo with using =>. It's very very very quotey. It quotes even subroutine names (as long as they are simply bare words), which is what a use constant ends up being. Either switch to comma instead of arrow, or invoke your subroutine:
    my %capitol_map = ( WISCONSIN() => "Madison", ILLINOIS() => "Springfield", ... );

    -- Randal L. Schwartz, Perl hacker
    Be sure to read my standard disclaimer if this is a reply.

Re: use constant for strings
by dws (Chancellor) on Apr 18, 2003 at 17:52 UTC
    I had assumed that in the hash creation, that the constants would be interpolated into their text values.

    The tokens WISCONSON and ILLINOIS are being treated as "barewords", thanks to '=>'. To force them to be interpeted, you can do something like

    my %capitol_map = ( (WISCONSON) => "Madison", ...
    or risk a bit of duplication and write
    my %capitol_map = ( WI => "Madison", ...
Re: use constant for strings
by perrin (Chancellor) on Apr 18, 2003 at 17:58 UTC
    I advise you not to use the "constant" module for this reason. It comes up in other places as well (double-quoted strings, for example) and forces you to think of these contants as subroutine calls rather than constant values. You're better off just using global variables for them instead.
Re: use constant for strings
by ChemBoy (Priest) on Apr 18, 2003 at 17:57 UTC

    The problem is your use of the stringifying comma (=>). The parser sees it following a bareword, and assumes that you want the string value "WISCONSIN" rather than the return value of the subroutine &WISCONSIN().

    There are various ways around this: you can change the fat comma to a regular comma, or you can change the bareword into something Perl recognizes as subroutine call (since that's what a constant is actually implemented as).

    my %capitol_map = ( WISCONSIN , "Madison", ILLINOIS , "Springfield", ... );
    or
    my %capitol_map = ( &WISCONSIN => "Madison", ILLINOIS() => "Springfield", ... );

    Update: it has been suggested that both of these have the potential to confuse future readers of your code, which I find a strong enough argument to recommend against using this particular construct. Use the skinny comma or dws's suggestion above, instead. </update>

    I was going to suggest %map = ( +WISCONSIN => "Madison"), but that doesn't actually do the trick with my perl. It does, however, work for getting keys back out of the hash, should you ever need to:

    print $capitol_map{+WISCONSIN};



    If God had meant us to fly, he would *never* have given us the railroads.
        --Michael Flanders

(jeffa) Re: use constant for strings
by jeffa (Bishop) on Apr 18, 2003 at 17:50 UTC
    UPDATE: i should have checked before i posted ... just do what merlyn said. :)

    Nope, that's not how constants work. Actually, a constant is really a Perl sub that returns the 'constant' value. Try using another hash:

    use constant WISCONSIN => "WI"; use constant ILLINOIS => "IL"; my %abbrev_map = ( WI => 'WISCONSIN', IL => 'ILLINOIS', ); my %capitol_map = ( WISCONSIN => "Madison", ILLINOIS => "Springfield", ); my $abbrev = "WI"; if ( exists $abbrev_map{$abbrev} ) { print "capitol = $capitol_map{$abbrev_map{$abbrev}}\n"; }
    Alternatively ... i have a strong suspicion that a CPAN module might exist that contains states and their capitols.

    UPDATE 2:
    Well, can't seem to find one. The closest matches are:

    which contain American states and their abbreviations, but no capitols .... hmmmm .... sounds like a new CPAN module is in the works.

    jeffa

    L-LL-L--L-LL-L--L-LL-L--
    -R--R-RR-R--R-RR-R--R-RR
    B--B--B--B--B--B--B--B--
    H---H---H---H---H---H---
    (the triplet paradiddle with high-hat)
    
Re: use constant for strings (unary plus)
by Aristotle (Chancellor) on Apr 18, 2003 at 18:40 UTC
    Besides the parens (around or following) and the ampersand, you can use an unary plus - the Perl way of disambiguating whether you are referring to an expression or something else.
    my %capitol_map = ( +WISCONSIN => "Madison", +ILLINOIS => "Springfield", ... );
    See rir's post below..

    Makeshifts last the longest.

      use constant KEY => "key"; my %h = ( +KEY => "data" , KEY() => "data",); $, = "\n"; print "Perl vers: $]", keys %h;
      Outputs:
      Perl vers: 5.006001 KEY key
        Gack, you're right.

        Makeshifts last the longest.

      personally, i don't like the unary plus. how does it make things less ambiguous? "plus" means "not an expression"? it's not clear. besides, we have sigils to remove ambiguity, anyway. i'd recommend &WISCONSIN, or if you're afraid of side effects (which won't occur with constants,) use &WISCONSIN(). more to write, but leaving less to the imagination.

      ~Particle *accelerates*

        I don't want to use the sigil simply because it disables the prototype and thus the constant-ish nature as mentions of the "constant" become regular runtime function calls (otherwise they'd be substituted at compile time). I might as well just use a variable.

        Seeing as the unary plus doesn't work here, the next best thing I'd prefer would be (WISCONSIN) - with the parens following it looks quite awkward IMHO.

        I like the unary plus in other situations and use it lots though - f.ex

        map +(foo($_, "bar"))[3], @baz;
        where I would otherwise be forced to use curlies
        map { foo($_, "bar"))[3] } @baz;
        or another extra pair of parens.
        map((foo($_, "bar"))[3], @baz);
        I don't like either of the latter two, though I realise quite a few people here will disagree. I think the last one at least is certain not to win any prizes for beauty..

        Makeshifts last the longest.

        i'd recommend &WISCONSIN, or if you're afraid of side effects (which won't occur with constants,)

        Then I guess you've never noticed how "constants" are implemented by default when one uses h2xs. I've seen &CONST cause warnings in real code because of that.

        But I don't use &CONST because I know it passes @_ to the CONST subroutine and I don't want whoever ends up maintaining my code wondering why I'm passing @_ in. Just because it (usually) works, doesn't mean it makes sense. (:

                        - tye
Re: use constant for strings
by Enlil (Parson) on Apr 18, 2003 at 17:55 UTC
    The constant you are assigning is WISCONSIN to WI not WI to WISCONSIN. That said there is also a difference between WI and "WI" as
    use constant WI => "WISCONSIN"; use constant ILLINOIS => "IL"; my %capitol_map = ( WISCONSIN => "Madison", ILLINOIS => "Springfield", ); my $abbrev = WI; if ( exists $capitol_map{$abbrev} ) { print "capitol = $capitol_map{$abbrev}\n"; } else { print "False" }
    will print the name of the capitol, and
    use constant WI => "WISCONSIN"; use constant ILLINOIS => "IL"; my %capitol_map = ( WISCONSIN => "Madison", ILLINOIS => "Springfield", ); #THE LINE BELOW WAS CHANGED my $abbrev = "WI"; if ( exists $capitol_map{$abbrev} ) { print "capitol = $capitol_map{$abbrev}\n"; } else { print "False" }
    will not.

    update:Hrm.. maybe I missed the fact that the OP might have wanted the WISCONSIN in the hash to become WI not the WI in the my $abbrev to become WISCONSIN ... so my answer would then be incorrect as i seem to be answering the wrong thing.

    -enlil

Re: use constant for strings
by rir (Vicar) on Apr 18, 2003 at 19:10 UTC
    Also consider Bolivia when you code stuff like this. :-)
Re: use constant for strings
by dragonchild (Archbishop) on Apr 21, 2003 at 14:10 UTC
    Personally, I wouldn't use this way of doing things. I would, instead, create an object that encapsulates this information, as thus: