In Perl 5.26, constant-based hash lookups appear to be resolved at compile-time, not runtime. How can I enforce it to be resolved at runtime?
Consider the following reduced testcase, boiled down from a hash-based state-machine I was trying to write, where the key is the state identifier and the value is the state function.
use constant {
STATE_1 => 1,
STATE_2 => 2,
};
my %fsm;
%fsm = (
STATE_1, sub {
$fsm{STATE_2}->(@_);
return STATE_2;
},
STATE_2, sub {
return STATE_1;
}
);
my $state = STATE_1;
$state = $fsm{$state}->();
Note that in STATE_1
, I'm trying to call the STATE_2
function.
However, at runtime I get this:
Can't use an undefined value as a subroutine reference at ./self-reference-hash.pl line 15.
Which indicates that the $fsm{STATE_2}->(@_);
line in STATE_1
is undefined. And indeed, at the time where this line first appears, the STATE_2
function isn't defined yet, but I was counting on hash lookups being resolved at runtime.
If I instead replace $fsm{STATE_2}->(@_);
with my $tmp = STATE_2; $fsm{$tmp}->(@_);
then it works as expected, which seems hacky.
Is there a cleaner way to do this?
The source of this problem is actually explained in Perl's doc about constant
, and it's not about runtime vs compile-time, but about Perl magically quoting barewords in some contexts:
You can get into trouble if you use constants in a context which automatically quotes barewords (as is true for any subroutine call). For example, you can't say
$hash{CONSTANT}
becauseCONSTANT
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 sayCONSTANT() => 'value'
(or simply use a comma in place of the big arrow) instead ofCONSTANT => 'value'
.
The listed workarounds resolve the issue.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With