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

第10回 Class::Meta::Express:もっと読みやすく,周囲への影響は最小限に

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

「シュガー関数=モダン」ではありませんが

「モダンPerlがわからない」と言われる大きな原因のひとつが,MooseJiftyに見られるシュガー関数,ドメイン特化言語(DSL)の氾濫にあることは衆目の一致するところでしょう。

前回紹介したJiftyでは,スキーマとアクション用にそれぞれひとつ,ディスパッチャ用にひとつ,テンプレート用にひとつ,という具合に都合3系統4種類のドメイン特化言語が使われていましたし,Mooseの場合も,アトリビュートや型の定義にメソッドモディファイアと,さまざまなところで独自の記法が用意されています。Catalystも,テストの際には独自のシュガー関数を使っていました。もちろん探せば似たような例はいくらでも見つかることでしょう。

このようなシュガー関数は,1998年にリリースされたPerl 5.5でコアに導入されたTestモジュールなどを見てもわかる通り,本来はモダンPerlに特有のものではありません。オブジェクト指向的な書き方が広まった影響でひと頃不遇をかこっていたのが,近年,あらためてその簡潔さが見直されるようになって,まるでモダンPerlの代名詞的な扱いをされるようになったものです。

もっとも,昔のシュガー関数と,いまのシュガー関数では,たしかに様子が異なる部分もあります。今回は,そのような違いに注目しながら,シュガー関数と呼ばれるもの,とりわけクラスやオブジェクトの設定に寄与しているものの裏側を少し覗いてみることにしましょう。

設定をまるごと放り込むタイプ

設定を行うためのシュガー関数としては,15年前,Perl 5の登場とともにうまれたExtUtils::MakeMakerのWriteMakefile(当時の表記にしたがえばwriteMakefile)あたりが最古層といってよいでしょう。

これも細かなところは時代とともにいろいろ変わりましたが,必要な情報をハッシュとして渡すと,内部で適切な変換を行ってMakefileを作成し,(ここでは利用していませんが)設定を格納したオブジェクトを返す,という基本は変わっていません。

use strict;
use warnings;
use ExtUtils::MakeMaker;

WriteMakefile(
    NAME         => 'Foo',
    VERSION_FROM => 'lib/Foo.pm',
    PREREQ_PM    => {
        'Some::Module' => '0.01',
    },
);

呼び出し元のクラスに設定が反映されるわけではない,という意味ではMooseやJiftyのシュガー関数とは趣が異なりますが,必要な情報のみ宣言すればあとはすべてモジュールのほうで良きに計らってくれる(内部のアルゴリズムを気にする必要はない)という点で,このWriteMakefile(や,それを利用するMakefile.PL)のインタフェースは非常に宣言的です。

また,このような仕組みは,モジュール内部の処理が変わってもインタフェース部分が変わらなければそのまま使い続けられるという意味で,非常に後方互換性を維持しやすいものであるともいえます。

複雑な設定に対応するのは大変

その一方で,この方式は,インタフェース部分しかユーザに開放されていない分,高度な処理を実行させようとすると設定が複雑なものになりがちなのがネックといえます。

たとえば,特定のOSの場合だけ依存モジュールを増やしたいとき,WriteMakefileの中にはしばしば三項演算子による条件分岐が入り込みます。

WriteMakefile(
    NAME         => 'Foo',
    VERSION_FROM => 'lib/Foo.pm',
    PREREQ_PM    => {
        'Some::Module' => '0.01',
        (($^O eq 'MSWin32')
            ? ('Win32::Module' => '0.01')
            : ()
        ),
    },
);

もちろんこれはもう少しベタに,このような書き方をしてもかまいません。

my %config = (
    NAME         => 'Foo',
    VERSION_FROM => 'lib/Foo.pm',
    PREREQ_PM    => {
        'Some::Module' => '0.01',
    },
);

if ($^O eq 'MSWin32') {
    $config->{PREREQ_PM}->{'Win32::Module'} = '0.01';
}

WriteMakefile(%config);

ただ,いずれにしてもこのようなやり方では,設定が複雑になってくるとインデントが深くなったり,かっこの数が増えて一覧性や可読性が悪くなりますし,場合によってはハッシュや配列の合成を行う余分なコードを書かなければならなくなってしまいます。

また,このようにハッシュを多用する設定の場合,定義ミスを見落としやすいのも欠点のひとつといってよいでしょう。内部的に必須属性のチェックを行っていればよいのですが,PREREQ_PMのように,かならずしも存在するとは限らないものの場合,つづりを間違えて「PREREQ」と書いてあっても,内部的にはふつう「PREREQ_PMはない」,と判断しておしまいになってしまいがちです。

設定を細分化して可読性を高めたタイプ

こういった問題に対するひとつの回答として登場したのが,2003年にリリースされたModule::Installです。

Module::Installを使うと,上の例はこのように書けます。

use strict;
use warnings;
use inc::Module::Install;

name 'Foo';
all_from 'lib/Foo.pm';
requires 'Some::Module'  => '0.01';
requires 'Win32::Module' => '0.01' if $^O eq 'MSWin32';

WriteAll;

ExtUtils::MakeMakerの例に比べると,細かな設定が1センテンスで指定できるようになったため,見た目はすっきりしました。また,今回はシュガー関数の名前を間違えると構文エラーになるのもポイントといってよいでしょう。

タイプ数については微妙なところですが,少なくともオブジェクト指向プログラミングで同等の処理を実現した場合に比べれば,目にも手にも,はるかにやさしいものになっていることは間違いありません。

著者プロフィール

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

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

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

コメント

コメントの記入