恥ずかしながら今日まで知らなかったんすっかり失念してたですが、要はラクダ本第3版の6.4.1 定数関数のインライン化の内容です。
過ちを犯した
use constant は関数と等価であることは有名です。それを利用して、定義した内容を実行時に別の値にしたくて以下のコードを書いたんですが、駄目でした。
use strict; use constant HOGE => 'A'; no warnings 'redefine'; *HOGE = sub () {'B'}; print HOGE, "\n";
これは A と出力されます。
理由
Deparseするとわかります。
% perl -MO=Deparse test.pl use constant ('HOGE', 'A'); no warnings; use strict 'refs'; *HOGE = sub () { 'B' } ; print 'A', "\n"; test.pl syntax OK
見事にインライン展開されてますね。これは、
と言う仕様によります。よって、コンパイルが終わって実行フェーズに移ってしまってからでは、定数を書き換えることは不可能です。
それでも書き換えたい
以下のようにコンパイル前に書き換えるしかありません。Bと出力されます。
use strict; use constant HOGE => 'A'; BEGIN{ no warnings 'redefine'; *HOGE = sub () {'B'}; } print HOGE, "\n";
しかし、モジュール構成が複雑になって来ると、どのタイミングでどの定数を利用する部分がコンパイルされるかを予測するのは非常に難しくなってきます。起動スクリプトの最初の最初で使う以外では、決してお勧めできる方法ではありません*2。
ちなみに、一応no warnings 書いてみてますが、Constant subroutine ... redefined のwarningはこれでは消えません。危険性を考えると当然かと思います。
追記
実は過去に全く同じことやってましたww 人間って忘れる生き物だなあ。