I banged my head on this problem all day, not understanding what the problem was. I finally figured it was time to enlist the monks' help. But when I condensed the problem down to a short test case exposing my dilemma, I naturally found what the problem was. This is therefore no longer a question, but rather a meditation, on the correct use of hash keys defined via use constant.

I was writing a CGI script that displayed a table of IP addresses under DHCP control. I wanted to display addresses that had been allocated, expired and/or in conflict. This was written as a table, with the background cell colour indicating the status. I then had to add a legend, to explain what the colours meant, and at the same time, I thought it would be nice to display the number of values encountered for each legend key.

The hash keys were nothing more than the HTML background colour to be used. Here's the problem, without running the code, what do you think the following will print out:

#! /usr/bin/perl -w use strict; use constant FOO => 'foo bgcolor'; use constant BAR => 'bar bgcolor'; sub keyname { return shift() % 2 ? FOO : BAR } # to simplify my( %g, %h ); $g{ keyname(20) } = 6; $g{ keyname(21) } = 12; $h{ FOO } = 18; $h{ BAR } = 24; print "g: $_ is $g{$_}\n" for keys %g; print "h: $_ is $h{$_}\n" for keys %h;

In the %h hash, I am assigning based on a defined constant. In the %g I am not sure which key to use. All I have is a value, so I call a subroutine that determines the appropriate key. As it turns out, and in hindsight it is perfectly obvious, the two sets of keys assignments are not equivalent. Here is the output:

g: foo bgcolor is 12 g: bar bgcolor is 6 h: BAR is 24 h: FOO is 18

When I was trying to debug the code, however, I couldn't understand why $g{FOO} was returning undef. I was having an impedance mismatch between how I thought constants should behave and how they do behave.

It turns out there are two ways to fix this. The problem is that $h{FOO} is using FOO as a string... literally 'FOO'. It is not calling the constant FOO subroutine. Bleagh! I could have either written the keyname thusly sub keyname { return shift() % 2 ? 'FOO' : 'BAR' } or else I could have written $g{+FOO} when refencing the hash. I tend to discount to first solution, as it appears to circumvent the whole point of using constants. I also thing that the +FOO is a bit of a wart that reveals the retrofittedness of constants in Perl, but I guess I can live with that, at least until Perl 6. So that's what my code now looks like.

<update> yes, as tye points out, there are many ways of writing +FOO. That's the point. You're exposing the gory details of how constants are hacked up in Perl, and I find the whole concept a bit ugly. Especially the way constants fall apart across subroutine boundaries.</update>

Mind you, some people hate using constants, but that's another story.

--
g r i n d e r

In reply to use constant hash keys by grinder

Title:
Use:  <p> text here (a paragraph) </p>
and:  <code> code here </code>
to format your post, it's "PerlMonks-approved HTML":



  • Posts are HTML formatted. Put <p> </p> tags around your paragraphs. Put <code> </code> tags around your code and data!
  • Titles consisting of a single word are discouraged, and in most cases are disallowed outright.
  • Read Where should I post X? if you're not absolutely sure you're posting in the right place.
  • Please read these before you post! —
  • Posts may use any of the Perl Monks Approved HTML tags:
    a, abbr, b, big, blockquote, br, caption, center, col, colgroup, dd, del, details, div, dl, dt, em, font, h1, h2, h3, h4, h5, h6, hr, i, ins, li, ol, p, pre, readmore, small, span, spoiler, strike, strong, sub, summary, sup, table, tbody, td, tfoot, th, thead, tr, tt, u, ul, wbr
  • You may need to use entities for some characters, as follows. (Exception: Within code tags, you can put the characters literally.)
            For:     Use:
    & &amp;
    < &lt;
    > &gt;
    [ &#91;
    ] &#93;
  • Link using PerlMonks shortcuts! What shortcuts can I use for linking?
  • See Writeup Formatting Tips and other pages linked from there for more info.