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

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

Perl界を牽引しているのはやはりネットまわり

Perlの世界でいま何が起こっているかを調べるには、最近どんなCPANモジュールが更新されているかを追いかけてみるのが簡単です。筆者も日頃からCPAN検索サイト更新情報や、typesterこと村瀬大輔氏のCPAN Recent Changesのフィードを読んでいますが、ここでは日頃みなさんもおそらく肌で感じているであろう傾向を具体的な数字に落とすために、cpan-uploadsという、CPANにアップロードされたファイルの情報が流れてくる受信専用のメーリングリストを利用して、この1年間にどのような名前のモジュールがアップロードされたかをまとめてみました。

このリストによると、2008年7月から2009年6月までの1年間にアップロードされたファイルの数は27,009個。そのファイル名(ディストリビューション名)を整理・分解して、共通の名前空間に属するものを出現回数順にまとめると、上位10傑はこのようになります。

 回数名前空間
1905Net
2614Catalyst
3532POE
4515WWW
5493Test
6434App
7401POE-Component
8369HTML
9355Data
10351MooseX

ひとつでもバグが見つかったら修正版をCPANにアップロードするという人もいれば、⁠不安定で実用に耐えない」という悪評が立たないように更新頻度を調整している人もいますから、出現回数が多いからといってかならずしも人気のある、重要な名前空間とは限りませんが、これを見る限り、やはりいまのPerl界はインターネットがらみ、特にCatalystの勢力が強いのがわかりますし、この連載ではまだ取り上げていませんが、20世紀のPerl界を締めくくる一大プロジェクトとなったPOEの堅調ぶりや、CPANモジュールの品質を支えるTest系モジュールの充実、以前取り上げたMooseの成長などもうかがえます。

ハック系のモジュールも毎日のように更新されています

ここまでは予想通りの結果といってよいのですが、興味深いのはその次、11位から20位までのリストです。

 回数名前空間
11318XML
12262Padre
13257DBIx
14252Class
15249Devel
16242Acme
17241CGI
18241Text
19231CPAN
20226Parse

12位のPadreは最近活発に開発が続けられているPerl製のIDE。16位のAcmeはいわゆる「ネタ」系のモジュールを入れておく名前空間なのですが、ここで筆者の目を引いたのは15位にランクインしたDevelという名前空間。

これは、1996年リリースのPerl 5.002で導入されたDevel::SelfStubberを筆頭に、2000年に初お目見えした5.6系列でコア入りしたDevel::DProfDevel::Peek2002年の5.007003でコア入りしたDevel::PPPortなど、もともとはかなり高度な開発者向けのツールを入れておく名前空間でした。

典型的なDevel系モジュールであれば、perlコマンドの-dスイッチといっしょに使うと「Devel::」という名前空間を省略できる、という特徴があるくらいPerlの内部に密着した名前空間ですから(たとえば2008年に登場して新たな定番となりつつあるDevel::NYTProfというプロファイラであれば、⁠perl -d:NYTProf <プロファイルを取りたいスクリプト>という形で実行できます⁠⁠、ふつうに考えれば2日に1回以上のハイペースで更新があるような場所ではないのですが、この数年は、従来のデバッグや開発支援といった分野だけでなく、前回話題にしたMooseX::Declareの背後にあるDevel::Declareなど、近年のモダンPerl界を特徴づける技術を支える黒魔術的なモジュールを入れておく場所としても活況を呈している様子がうかがえます。

もっとも、活況を呈しているのはDevelだけではありません。そのつもりで見ていくと、たとえば63位(年79回更新)には、Bという、Perl 5.10系列ではコアから外されてしまったPerlコンパイラ関連の名前空間が登場しますし、87位(62回更新)にはSubという、サブルーチン/メソッドまわりの名前空間が登場します。14位のClassや、62位(81回更新)のObjectといった名前空間のなかにもよりよい書き方を実現するためのツールが登録されていますから、均してしまえばそれこそ毎日ひとつやふたつはこの手のモジュールが目に入る、といってよいでしょう。

このようなモジュールはPerl内部の詳しい知識がないと使いこなせないものも少なくないので、なかなか日常業務のなかで取り入れるところまではいかないものですが、今回はそれぞれの名前空間からいくつか、話のネタになりそうなモジュールをピックアップしていきたいと思います。

モダン世代のソースフィルタ

Perlには1996年リリースのPerl 5.002からソースフィルタ用のAPIが組み込まれています。これは、もともとは(おもにソースコードを保護するために)圧縮・暗号化したファイルを動的に展開してコードを実行するために用意されたものなのですが(詳しくはPerlに同梱されているperlfilter.podをご覧ください⁠⁠、2000年にPerl 6の議論が始まってからはもっぱら理想的な書き方をPerl 5のコードに変換するためのツールとして使われるようになりました。古くからのPerlユーザであれば、宮川達彦氏が2001年に書いたFilter::Pyuutaというモジュールを思い出す方も少なくないでしょう。伊藤直也氏が以前紹介していたClass::HPLOOPerl6::Classesといったモジュールもソースフィルタを使ってPerl5らしからぬ表記を可能にしていました(いずれも2003年リリース⁠⁠。

比較的モダンに近いところでは、インギー・ドット・ネット(Ingy döt Net)氏が2005年にリリースしたTest::Baseなどの裏で使われているSpiffy(こちらは2004年リリース)がソースフィルタを活用していることで知られています。

ただ、このようなソースフィルタは、ソースコードを先頭から末尾まで一律に修正してしまうため、実装によっては本来壊してはいけないコードやデータまで汚染してしまうこともありました。

この問題を解決するため、ソースフィルタ用のAPIに手を加えて、ソースコードをパースしながらフィルタリングできるようにしたのが、2007年に開発が始まったDevel::Declareです。

その詳細についてはPODをご覧いただくとして、これもまた影響範囲をなるべく局所化しようという大きな流れの中に位置づけられるもの。いまでは前回紹介したMooseX::Declareだけでなく、Method::SignaturesTryCatchといったモダンなモジュールの裏方としても利用されています。

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

この手のモダンな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で今回取り上げたような名前空間を検索してみると、便利に使えるモジュールが見つかるかもしれません。

おすすめ記事

記事・ニュース一覧