モダンPerlの世界へようこそ

第11回 Devel::Declare:醜い部分を隠すための工夫あれこれ

この記事を読むのに必要な時間:およそ 4 分

終了処理が必要かどうかを判定する

この手のモダンなDevel系モジュールで比較的用途がわかりやすいものとしては,ユーヴァル・コグマン氏が2008年にリリースしたDevel::GlobalDestructionがあります。これは単にDESTROYがグローバルなガベージコレクションの中で呼ばれているかどうかを判定するもの(実際には,Pure Perlなコードでは利用できないPerl内部のフラグをXS経由で取り出しているだけ⁠⁠。Mooseなどを使って複雑なオブジェクトを構築したときに,不要な終了処理をスキップするときに使います。

スコープの最後に特殊な処理をしたいときは

Bの世界はDevel以上にPerl内部に密着した逸般人向けのものですが,2008年リリースのB::Hooks::EndOfScopeというモジュールを利用すると,スコープのコンパイルが終了した時点でコードを実行することができるようになります(実際の処理はヴィンセント・ピット(Vincent Pit)氏が2007年にリリースしたVariable::Magicという,名前からしていかにも黒魔術を使っていますといわんばかりのモジュールが行っています⁠⁠。

スコープのコンパイルが終了したとき,といわれてもあまりピンと来ないかもしれませんが,Devel::DeclareのPODには,Devel::Declareで宣言したメソッド末尾のセミコロンを省略するためにこのモジュールを使う例が紹介されています。

また,最新のCatalystでは,このモジュールを使って,make_immutableの方法が正しくなかった場合setup終了時に警告を表示するようにしています。

型グロブやno strictを見せない工夫

Devel系やB系に比べると,Sub関連のモジュールにはすぐに使える実用的なものもたくさんあります。

たとえば,リカルド・シグネス(Ricardo Signes)氏が2005年にリリースしたSub::Installや,氏がそれをベースに2006年にリリースしたSub::Exporterというモジュール。これらはもともと2005年5月にダミアン・コンウェイ氏がリリースしたSub::Installerというモジュールから発展したものなのですが,その目的は,Perlをある程度使えるようになった人がよくやる型グロブを使ったメソッドのインストールを隠蔽すること。

モダンPerlの世界ではとかくuse strictやuse warnings(あるいは同等の効果を持つ各種モジュールをロード)して変数宣言を強制するべし,という話が登場しますが,そう言いながら一方ではこんな「例外的な」コードを量産してきました。

use strict;
use warnings;

{   # 例外的な処理はせめてブロックの中に閉じこめましょう
    no strict 'refs';
    no warnings 'redefine';
    *{$package_name.'::'.$method_name} = sub { ... };
}

これはコードも汚くなりますし,教育上もよろしくない。かといってMooseのような大きなフレームワークを使うのは大げさすぎる,といった場合は,たとえばSub::Installを使えばこんな感じで処理を隠蔽できますし,

use strict;
use warnings;
use Sub::Install qw( reinstall_sub );

# もう no strict 'refs' は見えません

reinstall_sub({
    into => $package_name,
    as   => $method_name,
    code => sub { ... },
});

Sub::Exporterの場合は一手間かかりますが,

package MyClass;

use strict;
use warnings;
use Sub::Exporter -setup => {
    exports => [
        method_name => sub { return sub { ... } },
    ],
};

1;

のような形にして,呼び出し元で明示的にインポートしてやると,期待した無名関数を呼び出し元にインストールできます。

use strict;
use warnings;
use MyClass qw( method_name );

# これで __PACKAGE__->method_name() のようなコードが利用できます。

無名関数に名前を付ける

2004年リリースのSub::Nameを使うと,無名関数に名前を付けることができます。様子を調べるために,このようなスクリプトを実行してみてください。

#!perl
use strict;
use warnings;
use Carp;
use Sub::Name;

my $anon = sub { carp "__ANON__" };
$anon->();

my $named = subname "anonymous", sub { carp "anonymous" };
$named->();

このように無名関数がひとつだけの場合はあまりありがたみがありませんが,ドメイン特化言語などを構築して多数の無名関数を使うようになると,そのそれぞれに名前をつけられるこのモジュールのありがたみを感じられるようになります(Mooseなどで多用されています⁠⁠。

関数リファレンスの元の名前を調べる

逆に,2005年リリースのSub::Identifyを使うと,関数リファレンスがどの関数を参照しているかを調べられます。

use strict;
use warnings;
use Sub::Name;
use Sub::Identify ':all';

print sub_name(\&method), "\n"; # method
print sub_name(subname "foo", sub { return }), "\n"; # foo

sub method { return }

汚い部分を隠して読みやすく

Perlのコードは記号が多くて読みづらいとか,なにをやっているんだかわかりづらい,と言われることもありますが,そのようなわかりづらい部分はどんどんモジュールに切り出して隠していくのがモダンなやり方。モジュールにすることで,その汚い(バグが混入しやすい)部分を独立してテストできるようになるというメリットも出てきます。

依存モジュールが増えると再配布がやや面倒になる,というデメリットもありますが(特にPerl内部に密着したDevel系やB系にはコンパイラ必須のものが少なくないので,環境によっては使えないこともあります⁠⁠,自分のコードを見直して,⁠汚い処理をしているなあ」と感じる部分があったら,CPANで今回取り上げたような名前空間を検索してみると,便利に使えるモジュールが見つかるかもしれません。

著者プロフィール

石垣憲一(いしがきけんいち)

あるときは翻訳家。あるときはPerlプログラマ。先日『カクテルホントのうんちく話』(柴田書店)を上梓。最新刊は『ガリア戦記』(平凡社ライブラリー)。

URLhttp://d.hatena.ne.jp/charsbar/