Beefy Boxes and Bandwidth Generously Provided by pair Networks
P is for Practical
 
PerlMonks  

strict "vars" mode for hash key literals?

by perlancar (Hermit)
on Oct 18, 2016 at 15:32 UTC ( [id://1174216]=perlquestion: print w/replies, xml ) Need Help??

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

use strict "vars" is great and makes me more comfortable when writing Perl code compared to when I'm writing PHP, Python, or Ruby.

However, since I utilize hashes a lot, typos made in the hash key literals that went undetected until runtime have at least one to several times bitten me.

What would be the closest thing we have in Perl/CPAN for something like (pseudo-/imagined code below)?

my %hash1; myhk @hash1{qw/foo bar baz/}; # define hash keys to use say $hash{foo}; # ok, declared say $hash{bah}; # compile-time error, undeclared say $hash{$var}; # ok, not literal

Replies are listed 'Best First'.
Re: strict "vars" mode for hash key literals?
by kennethk (Abbot) on Oct 18, 2016 at 15:41 UTC
    The Hash::Util CORE module handles your use cases. They incur a run time penalty, so I usually use them in a development context only.
    use Hash::Util 'lock_keys'; my %hash; lock_keys %hash, qw/foo bar baz/; # define hash keys to use say $hash{foo}; # ok, declared say $hash{bah}; # compile-time error, undeclared say $hash{$var}; # ok, not literal
    See *lock_keys*.

    #11929 First ask yourself `How would I do this without a computer?' Then have the computer do it the same way.

      Wouldn't that be a runtime error? After all, the hash isn't even locked until lock_keys is executed at runtime.

      Also, I think there is no performance penalty (except to the call to lock_keys), since I believe all lock_keys does is set the read-only flag on the hash, which is checked regardless of whether you've set lock_keys or not.

        Yes, I should have clarified that it is a runtime error, but it is not a silent error. Thank you for bringing that up.

        You understand perlguts far better than I, but there is a measurable penalty for interacting with a hash with locked keys:

        #!/usr/bin/perl use strict; use warnings; use 5.10.0; use Hash::Util 'lock_keys'; use Benchmark 'cmpthese'; lock_keys my %hash1, qw/foo bar baz/; my %hash2; cmpthese(1e2, { 'lock' => sub { for (1 .. 1e5) {$hash1{foo}++;$hash1{bar}++;$ +hash1{baz}++;} }, 'no_lock' => sub { for (1 .. 1e5) {$hash2{foo}++;$hash2{bar}++;$ +hash2{baz}++;} }, });
        yields
        Rate lock no_lock lock 61.6/s -- -8% no_lock 66.8/s 8% --

        #11929 First ask yourself `How would I do this without a computer?' Then have the computer do it the same way.

        Yes, it isn't trapped at compile time:

        $ perl '-mfeature=say' -cw 1174218.pl Name "main::var" used only once: possible typo at 1174218.pl line 7. 1174218.pl syntax OK
Re: strict "vars" mode for hash key literals?
by BrowserUk (Patriarch) on Oct 18, 2016 at 15:45 UTC

    See Hash::Util for the concept of 'restricted hashes'.

    That said, I don't know of anyone who has used perl for more than a few weeks that feels the need to use them; nor in the 12 or 13 years since 5.8, have I encountered any code in the wild that uses them.


    With the rise and rise of 'Social' network sites: 'Computers are making people easier to use everyday'
    Examine what is said, not who speaks -- Silence betokens consent -- Love the truth but pardon error.
    "Science is about questioning the status quo. Questioning authority". I knew I was on the right track :)
    In the absence of evidence, opinion is indistinguishable from prejudice.

      I use them to great effect with results fetched from an SQL query. There, locked hashes have made it very obvious when my code tries to access elements which have been renamed in the SQL query, which has reduced the time to debug these things.

      But when I'm at liberty to name the hash entries myself, I don't feel the need for restricted hashes.

      While I agree that I've never encountered restricted hashes in real code, and have never felt like I should reach for them, I also cannot say that I have never been bitten by problems that they could have prevented. Unfortunately, unlike use strict, they are a feeble solution that is inelegant to wield.


      Dave

        I certainly wasn't counsellings against their use; just noting that I am unable have no basis upon which to recommend for them.


        With the rise and rise of 'Social' network sites: 'Computers are making people easier to use everyday'
        Examine what is said, not who speaks -- Silence betokens consent -- Love the truth but pardon error.
        "Science is about questioning the status quo. Questioning authority". I knew I was on the right track :)
        In the absence of evidence, opinion is indistinguishable from prejudice.
Re: strict "vars" mode for hash key literals?
by Eily (Monsignor) on Oct 18, 2016 at 17:18 UTC

    ++ for restricted hashes. But as pointed by ikegami, they do not catch the exception at compile time.

    There might be a source filter somewhere on CPAN that does what you want, if you're ok with using a source filter. Parsing the code for /[$](\w+)[{](\w+)[}]/ and making sure that $2 is a valid key for hash $1 would probably be easy and safe enough.

    Otherwise, there's the solution of using constants. Either by using an array instead of the hash:

    use strict; use warnings; use constant { FOO => 0, BAR => 1, BAZ => 2 }; $var = BAZ; my @array; say $array[FOO]; # OK say $array[BAH]; # Bareword "BAH" not allowed while "strict subs" in +use say $array[$var]; # OK
    Or never allowing the bareword interpretation for a hash key by adding a + (there's no way to disable ALL barewords is there?)
    use strict; use warnings; use constant { FOO => "foo", BAR => "bar", BAZ => "baz" }; my %hash; say $hash{+FOO}; # OK say $hash{+BAH}; # Bareword "BAH" not allowed while "strict subs" in +use say $hash{+shift} # Bonus

Re: strict "vars" mode for hash key literals?
by hippo (Bishop) on Oct 18, 2016 at 15:42 UTC

Log In?
Username:
Password:

What's my password?
Create A New User
Domain Nodelet?
Node Status?
node history
Node Type: perlquestion [id://1174216]
Front-paged by Corion
help
Chatterbox?
and the web crawler heard nothing...

How do I use this?Last hourOther CB clients
Other Users?
Others musing on the Monastery: (4)
As of 2024-04-25 14:04 GMT
Sections?
Information?
Find Nodes?
Leftovers?
    Voting Booth?

    No recent polls found