strict無効化の誤謬
シンボルテーブルを操作するときに"no strict 'refs'"で一時的にstrictを無効化することはよくあるが,デバッグしにくいバグが紛れ込む可能性がある。
たとえば,以下のようにアクセサを動的に生成するコードはCPANのそこかしこにある。
sub make_accessor{ my($class, $property) = @_; no strict 'refs'; # simple read-only accessor *{$class. '::' . $property} = sub{ my($self) = @_; return $self->{$property}; } }
このようなコードによって生成されたメソッドを,正しくオブジェクトに対して使う分には問題ない。しかし,このメソッドをクラスメソッドとして呼び出すと,グローバル変数${$self}を参照し,その値をハッシュリファレンスとみなし,その一要素を返す($self->{$property})。クロージャ内部で"strict 'refs'"が無効になっているからである。
strictの恩恵を受けるためには,クロージャ内部で再度strictを有効にする,一時変数を用意してスコープを分ける,サブルーチンインストーラ*1を使うなどの方法がある。
# クロージャ内部で再度有効にする no strict 'refs'; *{$class.'::'.$property} = sub{ my($self) = @_; use strict 'refs'; return $self->{$property}; }
# 一時変数を用意してスコープを分ける my $code = sub{ my($self) = @_; return $self->{$property}; } no strict 'refs'; *{$class.'::'.$property} = $code;
# サブルーチンインストーラを使う use Data::Util qw(install_subroutine); install_subroutine $class => ( $property => sub{ my($self) = @_; return $self->{$property}; }, );
実際には,これはクロージャ内部でデリファレンスさえしなければ問題にはならないのだが,strictが無効になっている部分を注意深くチェックするよりは,可能な限りstrictを有効にしたほうがずっと簡単だ。
具体的なコードを見てみると,たとえばDBIx::Class v0.8099_05およびそれ以前のDBIx::Class::Relationshop::ManyToMany*2で広範囲に及ぶstrictの無効化が見られる。"no strict 'refs'"はよく使われる分,うっかり使いすぎることも少なくないようだ。
(追記:ブクマより,参考文書にid:fbis氏の記事を追加)
参考文書:
- 『Perl ベストプラクティス』 18.10 「制約の無効化 - 制約または警告を無効にする場合は,明示的に,段階的に,最も狭いスコープで行う」
- Unknown::Programming - 型グロブとシンボリックリファレンスでメソッド定義するときの注意点
*1:Sub::InstallやClass::MOP,Data::Utilなどが提供している。
*2:http://cpansearch.perl.org/src/RIBASUSHI/DBIx-Class-0.08099_05/lib/DBIx/Class/Relationship/ManyToMany.pm