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}becauseCONSTANTwill 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