Beefy Boxes and Bandwidth Generously Provided by pair Networks
The stupid question is the question not asked
 
PerlMonks  

Using constants as hash keys

by choroba (Cardinal)
on Jan 04, 2018 at 19:03 UTC ( [id://1206712]=perlmeditation: print w/replies, xml ) Need Help??

Context: a StackOverflow question on how to use constants as hash keys.

Note that the module itself mentions the following:

For example, you can't say $hash{CONSTANT} because CONSTANT will be interpreted as a string. Use $hash{CONSTANT()} or $hash{+CONSTANT} to prevent the bareword quoting mechanism from kicking in. Similarly, since the => operator quotes a bareword immediately to its left, you have to say CONSTANT() => 'value' (or simply use a comma in place of the big arrow) instead of CONSTANT => 'value'.

The OP used &CONSTANT => 'value' which works but doesn't inline the constant (i.e. expand it during compile time).

ikegami pointed me to a different way which I found more pleasing than CONSTANT() which, as he rightly noted, leaks the internal implementation of constants.

use constant A => 12; my %hash = ( (A) => 'twelve' ); # beautiful

I wanted to verify it behaves exactly the same, so I tried running it through B::Deparse, B::Terse, and B::Concise.

m=Deparse diff <(perl -MO=$m -e 'use constant A => 12; my %hash = ( A() => "twel +ve")') \ <(perl -MO=$m -e 'use constant A => 12; my %hash = ( (A) => "twel +ve")')
says the code is identical.

Terse needs some tweaking to skip the pointers:

m=Terse diff <(perl -MO=$m -e 'use constant A => 12; my %hash = ( A() => "twel +ve")' \ | perl -pe 's/0x\w+/X/g') \ <(perl -MO=$m -e 'use constant A => 12; my %hash = ( (A) => "twel +ve")' \ | perl -pe 's/0x\w+/X/g')

But Concise shows a slight difference:

m=Concise diff <(perl -MO=$m -e 'use constant A => 12; my %hash = ( A() => "twel +ve")') \ <(perl -MO=$m -e 'use constant A => 12; my %hash = ( (A) => "twel +ve")') 7c7 < 4 <$> const[IV 12] s*/FOLD ->5 --- > 4 <$> const[IV 12] sP*/FOLD ->5

From the documentation it seems the P just means that A was parenthesized. I can imagine this information could be valuable to Perl (e.g. in the LHS of an assignment, but the structures of scalar versus list assignments are much more different); but probably not in this case.

Update: Topics for meditation include other possible syntaxes (e.g. A ,=> 12), personal preferences, explanation of the Concise's output, etc.

($q=q:Sq=~/;[c](.)(.)/;chr(-||-|5+lengthSq)`"S|oS2"`map{chr |+ord }map{substrSq`S_+|`|}3E|-|`7**2-3:)=~y+S|`+$1,++print+eval$q,q,a,

Replies are listed 'Best First'.
Re: Using constants as hash keys
by Eily (Monsignor) on Jan 05, 2018 at 13:52 UTC

    I think the A() has at least has the advantage of being 100% clear to whoever reads the code (no one will think this might still be a bareword).
    A , "Value" won't match the style of other key/pair values (or the standard notation), and ,=> looks enough like a typo (or even syntax error if you're not familiar with perl) that someone will be tempted to correct it. I agree that (A) is a beautiful solution :)

    There are of course many silly alternatives, like %hash = reverse ("Value" => A, "Value" => B);, or using $::{A}->$*, but the only other correct solution I can think of is prepending the package name.

    use strict; use warnings; use Data::Dump qw( pp ); use constant A => "One"; use constant B => "Two"; package Consts { use constant C => "Three"; } pp { main::A => 1, ::B => 2, Consts::C => 3, }; __DATA__ { One => 1, Three => 3, Two => 2 }
    And having your constants in a specific namespace may be a good idea in the first place.

Re: Using constants as hash keys
by karlgoethebier (Abbot) on Jan 05, 2018 at 13:41 UTC

    Very nice. Thanks for sharing this choroba.

    The inevitable little addendum:

    Best regards, Karl

    «The Crux of the Biscuit is the Apostrophe»

    perl -MCrypt::CBC -E 'say Crypt::CBC->new(-key=>'kgb',-cipher=>"Blowfish")->decrypt_hex($ENV{KARL});'Help

      Hello karlgoethebier,

      I’ve been a fan of Const::Fast since I discovered it recently in the Perl Advent Calendar 2017. But constant does have one important advantage over Const::Fast: it is applied at compile time, whereas Const::Fast takes effect only at run time. So, in this respect, Const::Fast suffers from the same limitation, identified by choroba, as the &CONSTANT syntax for the constant pragma — the constant isn’t inlined:

      0:59 >perl -Mstrict -MData::Dump -MConst::Fast -we "use constant A => + 12; const our $B => 13; my %h = ( (A) => 'twelve', $B => 'thirteen') +; dd \%h;" { 12 => "twelve", 13 => "thirteen" } 0:59 >perl -Mstrict -MO=Deparse -MData::Dump -MConst::Fast -we "use c +onstant A => 12; const our $B => 13; my %h = ( (A) => 'twelve', $B => + 'thirteen'); dd \%h;" BEGIN { $^W = 1; } use strict; use Data::Dump; use Const::Fast; use constant ('A', 12); &const(\our $B, 13); my(%h) = (12, 'twelve', $B, 'thirteen'); dd(\%h); -e syntax OK 0:59 >

      (AFAICT, this also makes Const::Fast unsuitable in situations where one wants to write code like this:

      use constant DEBUG => 0; ... perform_some_debugging_operation() if DEBUG; ...

      and have the compiler remove the expensive operation when it sees that DEBUG is false.)

      Update: Changed perform_expensive_debugging_operation() to perform_some_debugging_operation(), because, as Eily pointed out in a private message, the cost of the operation is irrelevant if DEBUG (or $DEBUG) is false.

      Cheers,

      Athanasius <°(((><contra mundum Iustus alius egestas vitae, eros Piratica,

        "...the same limitation..."

        Yes. Thanks. Unfortunately this is true. And i begin to see it clearly now. Best regards, Karl

        «The Crux of the Biscuit is the Apostrophe»

        perl -MCrypt::CBC -E 'say Crypt::CBC->new(-key=>'kgb',-cipher=>"Blowfish")->decrypt_hex($ENV{KARL});'Help

Re: Using constants as hash keys
by Anonymous Monk on Jan 06, 2018 at 13:46 UTC
    One of the more-serious 'gotchas' of the Perl language is that it really doesn't have the notion of a "compile-time constant," as other languages do. The semantics were "grafted on," as is the case with so many other features of this venerable language. But, in so doing, they became misleading. You might well not actually know the implementation or the behavior of the language-constructs that you are looking at.
      it really doesn't have the notion of a "compile-time constant,"

      I have to say this is incorrect. Perl has Constant Functions and their effect is compile-time, note how print "foo" is optimized away completely  (edited slightly for brevity):

      $ perl -MO=Deparse use constant DOFOO => 0; my $DOBAR = 0; print "foo" if DOFOO; print "bar" if $DOBAR; __END__ use constant ('DOFOO', 0); my $DOBAR = 0; '???'; print 'bar' if $DOBAR;
      But, in so doing, they became misleading. You might well not actually know the implementation or the behavior of the language-constructs that you are looking at.

      I find this misleading because it makes it sound like there is some undefined behavior here. Whether or not a function was inlined or not should be transparent to the user. And what choroba is writing about has more to do with the autoquoting happening on the LHS of the fat comma, which is also well defined and has nothing to do with whether it's a constant function:

      use Data::Dump; use constant FOO => 123; my $x = 455; sub BAR { ++$x } my %h = ( FOO => "A", BAR => "B", (FOO) => "C", (BAR) => "D", ); dd \%h; __END__ { 123 => "C", 456 => "D", BAR => "B", FOO => "A" }
      The semantics were "grafted on,"

      Constant functions have been around for something like 20 years, at least as far as I can tell from a quick look at the documentation. I'm willing to concede a couple points: 1. constant, or rather readonly, variables aren't easily usable without somewhat obscure syntax (*PI=\3.14;, see Symbol Tables) or a module like Readonly, 2. readonly variables don't have the same compile-time effect, and 3. constant functions require disambiguation when used as hash keys. However, taking the post as a whole, I'm sorry but IMO it is more FUD than fact.

        I do hope the points Anon raised are among "topics for meditation", since much of the talk is about constant folding at the compile time. Anyhow, there's little to fault in his overall assessment.

        Consider the enumerated constant in C:

        enum { FOO = 2 }; enum { FOO = 3 }; // error: redefinition of enumerator
        Contrast this with Perl:
        *FOO = \1.2; *FOO = \3.4; print our $FOO; # prints 3.4
        Perl has no means to protect an identifier in a given scope. This is useful for example in guarding against file level symbol clashes. (Perl isn't C, of course, but some perl modules do export lots of symbols: Fcntl, POSIX, ....)

        Another sign of the problem is that constants are commonly used via hashes to keep things tidy and structured. A particular case would be the use Config. If you have conditional code-paths that depend on, say, $Config{longsize}, then these tests are not folded during compilation even though %Config::Config is supposedly read-only.

Log In?
Username:
Password:

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

How do I use this?Last hourOther CB clients
Other Users?
Others contemplating the Monastery: (7)
As of 2024-04-16 12:54 GMT
Sections?
Information?
Find Nodes?
Leftovers?
    Voting Booth?

    No recent polls found