taro-nishinoの日記: 迷信: Mooseは無用な従属物である
以前にも書いたことがありますが、私の周辺でPerlに習熟していない人には理屈もへったくれもなく、Mooseを使うように言ってます。それだけでは暴君なので、Moose::Manual::Unsweetenedあたりを読んでみたらとアドバイスします。これを読んで真意が分かるはずだと思っていました。ところが、後に納得したかと聞くと、逆に反論して来る人もいました。どこで仕入れて来たのか分かりませんが、スタートアップ時間がどうたらこうたら云々。そんなもの達人になってから言えと言いたいところを我慢して、Moose::Manual::Unsweetenedの感想を聞きますと、どうも読んだのかそうでないのかはっきりしません。Moose::Manual::Unsweetenedを私は何回も読み返しているのですが、ちょっとインパクトが弱いように思いました。マニュアルだから刺激が少ないのは当り前です。そこで、何かいいのはないかと考え、Jonathan Rockway氏のエッセイ″Myth: Moose is an unnecessary dependency″です。以下、その私訳を載せておきます。なお、コードの中に、MooseX::AttributeHelpersを使用している所がありますが、今ではこの機能そのものはMoose本体に移ってMoose::Meta::Attribute::Nativeとなっています。勿論MooseX::AttributeHelpersは今も存続しておりますが、初めてアトリビュートヘルパーを使う場合は別途インストールする必要は無くなりました。
迷信: Mooseは無用な従属物である
2007年11月26日 Jonathan Rockway
人がMooseをアプリケーション内に使用又は導入がこわくて出来ない従属物であると名指しすることは、私にいつも奇妙な感じを与える。私が聞いたことのある議論の一つは、「仕事に関係の無い従属物で、事を過度に複雑にしたくない」。その代わりが無用なコードとテストをアプリケーションに追加するのであるから、この見解は間違った考えだ。ベストなコードは保守する必要の無いコードである。
ともかくも、出来れば一例が私の見解を明確にすることを願う。My::Barオブジェクトである"bar"と呼ばれる一つのフィールドを持つ小さなクラスを考えよう。
Mooseを用いれば、単に以下の通り。
package My::Class;
use Moose;
use My::Bar;
has 'bar' => (
is => 'rw',
isa => 'My::Bar',
);
それから、以下のことを言える。
use My::Class;
my $class = My::Class->new( bar => My::Bar->new );
print $class->bar; # or whatever
Mooseでないアプローチを見てみよう。
package My::Class;
use strict;
use warnings;
use My::Bar;
use Carp;
use Scalar::Util qw(blessed);
sub new {
my ($class, %args) = @_; # crappy error message if %args is odd
croak 'bar must be a My::Bar'
unless blessed $args{bar} && $args{bar}->isa('My::Bar');
my $self = { bar => $args{bra} };
return bless $self => $class;
}
sub bar {
my $self = shift;
my $setting = scalar @_ > 0;
my $new_bar = shift;
if ($setting){
croak 'bar must be a My::Bar'
unless blessed $new_bar && $new_bar->isa('My::Bar')
$self->{bar} = $new_bar;
}
return $self->{bar};
}
ほら、納得した。21行以上のコードを使って、殆ど同じ機能を持つ。おや、君はnewの中にタイポを見つけたかい? うんざりするisa_okスタイルのテストを君はまだ書いていないのだから、当然見つけていない。少し書いてみよう。
#!/usr/bin/env perl
use Test::More plan => 'no_plan'; # I'm lazy.
use Test::Exception;
use ok 'My::Class';
use My::Bar;
{
# ideal case
my $bar = My::Bar->new;
my $c;
lives_ok { $c = My::Class->new( bar => $bar ) };
isa_ok $c, 'My::Class', '$c';
isa_ok $c->bar, 'My::Bar', '$c->bar';
}
{
# not a bar
my $bar = 42;
throws_ok
{ My::Class->new( bar => $bar ) }
qr/bar must be a My::Bar/;
}
# test for bar == ref but not blessed
# test for bar blessed, but not isa My::Bar
# test get
# test set (like above, but for bar insead of new)
ここまででnewが働くのを見るだけのために25行を書いて来たが、newのすべてを試してさえいない! Mooseを用いれば、私が何らコードを書いていないのだから、これらのテストを書く必要が無い。他の誰かがコードとテストを書いたのだから、私のクラスが働くのを私は既に知っている。OOを働かすメカニズムを考えるのではなくて、私はMy::Classを書くことに専念出来る。
Class::Accessorを使用していないことに駄々をこねる観衆の内の悪口を言いふらす人に対しては、Class::Accessorは自動的に引数の型をチェックしないことを気づかせたい。newとbarをオーバーライドすることによって、この辺りをハック出来るが、それはいくらか自動生成アクセサの全体の目的を撃破する。私は御免するだろう。
勿論、これは単にMoose氷山の一角である。最も簡単な場合においてでさえ、時間とエネルギーの多くを節約するが、委任、厳格な型制約、またはイントロスペクションを欲しがり始める時には、Mooseは更に光輝く。
もう一つの例。
package CPAN::Distribution;
use Moose;
use MooseX::AttributeHelpers;
has 'module_versions' => (
metaclass => 'Collection::Hash',
is => 'rw',
isa => 'HashRef[Str]',
default => sub { {} },
provides => {
count => 'module_count',
keys => 'modules',
get => 'module_version',
set => 'add_module',
},
);
そして私は以下のように言える。
my $dist = CPAN::Distribution->new;
$dist->module_versions( { 'Foo::Bar' => '42', ... } );
$dist->add_module( 'Another::Module' => '0.01' );
my @modules = $dist->modules; # Foo::Bar, Another::Module
$dist->module_count; # 2
$dist->module_version('Foo::Bar'); # 42
これすべてが宣言の12行からである。しかも、他の誰かが実際のコードを書いた(そしてテスト済み)ので、私は動くことを知っている。
最後になるが、Mooseは、実際の問題から君をそらせる、無用な従属物ではない。正に反対である。すなわち、3つのコアでないモジュールを君のアプリケーションに加えるコストを払って、君のコードとテストを書く時間を節約する従属物なのだ。
これはPerlであり、″There's More Than One Way To Do It″であるから、自由にMooseを無視してよい。しかし、君の顧客がOO車輪の再発明に金を費やさない会社に切替える時、吃驚するな。
迷信: Mooseは無用な従属物である More ログイン