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

All, I would like some advice/appriasal or even well considered criticism on an development approach we started with but is now looking clunky. The perl compiler can easiliy let though typos in hash references and these errors can take forever to filter throug - often in production for example.
$r->{thisid} = 10; $r->{this_id} = 10;
As part of coding quality control we set up a constants file for record references shared between modules (and developers). So the above is rendered
use constant ID =>'thisid'; . . $r->{(ID)} = 10; . .
Incorrect field indexes entered by bleary eyed developers in such as
$r->{(THISID)}
Will be immediately picked up by the compiler in development and not in production. ..... However This constants file in near 400 lines long and growing. Do the monks have any opinion on whether this was a reasonable strategy or is there a better way to ensure the same level of development robustness? Benno UPDATE - what I actually did Firstly I reread the perl documentation on exporting. Then I split the constants file into logical modules. In my case these were XML tokens, default setting and system tokens. Then I went through each module and changed the blanket includes to be more specific. Since there were 70 modules I wrote some helper scripts to find where the constants were being used and the automatic generation of the header include from within VI using the .!sh command. If anyone wants these I'll post later.

Replies are listed 'Best First'.
Re: Advice on Global Constants
by perrin (Chancellor) on Jun 16, 2007 at 15:30 UTC

    There are a number of ways to do this. One is to lock the keys of the hash so it won't allow you to add new ones. That would catch your typos.

    What you're doing doesn't look too bad, except that I don't know why you're trying to put them all in one file. Each hash should have its keys defined in the place where the hash is defined, or else you'll have a hard time knowing which of the thousand possibly keys apply to the hash you want to use.

Re: Advice on Global Constants
by FunkyMonk (Bishop) on Jun 16, 2007 at 16:13 UTC
    Have you considered using restricted hashes? lock_keys and unlock_keys are found in Hash::Util.

    From the pod:

    5.8.0 introduces the ability to restrict a hash to a certain set of keys. No keys outside of this set can be added. It also introduces the ability to lock an individual key so it cannot be deleted and the value cannot be changed.
Re: Advice on Global Constants
by wind (Priest) on Jun 16, 2007 at 16:32 UTC
    I would advise against this type of technique. Hashes are very powerful data structures, and so it's tempting to use them for a lot of varying purposes. But as you stated, this introduces risk in the misspelling of keys.

    Based off the usage that you described, it sounds like what you actually want are classes. There are lots of cpan modules that make the defining and accessing of class fields very easy. This is one good one: Class::Accessor

    Another typical usage of hashes are named parameters for subroutines. There are also lots of validation techniques for this as well. But here is another module that I find effective: Params::Validate

    Ultimately, your concern about the misnaming of hash keys is alleviated when you recognize that very rarely should you ever be explicitly naming the keys. Algorithms rarely explicitly name the keys. And any other uses have their risk reduced when you put the values in scalars as soon as possible. To remove the final smidge of risk, simply have test scripts for all your core libraries anyway.
Re: Advice on Global Constants
by shmem (Chancellor) on Jun 17, 2007 at 09:35 UTC
    To relieve the immediate pain, first thing I would do is identifiying the use of various hash keys among the file set of the application in question and move the shared keys into *.pm files which export those constants, to be used by those files using the constants - "divide et impera" - much like C header files. That would help with the 'single file issue', but it might introduce more complexity at another level.

    Next you could consider restricting the key set of a hash via Hash::Util, or using accessors, probably as lvalue subs, e.g.

    sub this_id :lvalue { $_[0]->{this_id} = $_[1] if @_ > 1; $_[0]->{this_id}; }

    Then you can say

    $r->thisid = 10;

    but for this notation, $r has to be blessed into the package this constant lvalue sub is defined in.

    update: corrected sub

    --shmem

    _($_=" "x(1<<5)."?\n".q·/)Oo.  G°\        /
                                  /\_¯/(q    /
    ----------------------------  \__(m.====·.(_("always off the crowd"))."·
    ");sub _{s./.($e="'Itrs `mnsgdq Gdbj O`qkdq")=~y/"-y/#-z/;$e.e && print}
Re: Advice on Global Constants
by apl (Monsignor) on Jun 16, 2007 at 15:11 UTC
    I strongly suggest you pick up a copy of _Perl Hacks_ http://www.oreilly.com/catalog/perlhks/. I remember there is an item in there about validating hash references, but my copy is at the office. If no one responds by Monday morning, I'll post the relevant information then.
Re: Advice on Global Constants
by andreas1234567 (Vicar) on Jun 17, 2007 at 09:53 UTC
    This constants file in near 400 lines long and growing.
    Apart from shmem's advice, I would recommend the Readonly module. I would probably create a scalar variable using Readonly for each constant and export those from a separate module.
    --
    print map{chr}unpack(q{A3}x24,q{074117115116032097110111116104101114032080101114108032104097099107101114})
      Guys, This was my first post on this site and I would like to say I am amazed by the quality of replies. I'm still digesating them. Thanks a heap. The order has a new convert.
Re: Advice on Global Constants
by syphilis (Archbishop) on Jun 17, 2007 at 12:47 UTC
    Benno

    Richie ?

    Cheers,
    Rob