=encoding utf8 =pod =head1 題名 Moose::Manual::FAQ - Mooseについてのよくある質問 =head1 よくある質問 =head2 モジュールの安定度 =head3 Mooseは「実用レベルに達して」いますか はい! 誰でも知っているような有名サイトの中にもMooseを使って高トラフィックなサービスを構築しているところがたくさんあります。ほかにもMooseを本番環境で使っている人は数え切れないほどいます。 これを書いている時点で、Mooseは数百個のCPANモジュールの依存モジュールになっています。L =head3 MooseのAPIは安定していますか はい。95%のユーザが利用するシュガー関数のAPIについてはB<非常に安定>していますし、変更があってもB<100%後方互換にします>。 メタAPIについてはそれほど盤石ではありません。一部については効率や一貫性を向上させるために微調整する権利を留保します。ただし、軽々しく変更することはしません。機能を廃止する際にはかならず周知期間を設けていますし、みなさんのコードを壊してばつの悪い思いをするのは「本当に」いやなことだと思っています。リファクタリングしたときにうっかりみなさんのコードを壊してしまうような変更が入らないようにするいちばん確実な方法は、テストケースを送っていただくことです。 =head3 Mooseは遅いと聞きましたが本当ですか これも答えづらい質問ですが、答えはイエスであり、ノーでもあります。 最初にお断りしておくと、世の中にタダで手に入るものは「ありません」。また、Mooseの機能の中には、たしかにほかの機能よりコストのかかるものもあります。ただし、B<使った機能の分しかコストを請求しない>のがMooseのポリシーでもあります。私たちはできる限りの努力を払って、コードを実行するときに使っていない機能のせいで余計な負荷がかかることはないようにしています。もちろんMooseを使うこと自体いくらかのオーバーヘッドをともなうことですが(もっとも、そのほとんどはコンパイル時のものです)、いまのところ速度が必要な方にはいくつかのオプションがあります。 現在利用できるオプションとしては、クラスを不変化して高速化するという方法があります。こうするとコンパイル時のコストは多少大きくなりますが、実行時の速度(特にオブジェクトの生成時)はかなり速くなります。これは次のようなコードで実現できます。 MyClass->meta->make_immutable(); 私たちは定期的にLが熱暴走を起こしそうなところをXSで書き直しています。また、誰でも非常に高速なオブジェクト指向を楽しめるように、現在フロリアン・ラグヴィッツ(Florian Ragwitz)とユーヴァル・コグマン(Yuval Kogman)がアクセサやインスタンスを直接Cにコンパイルする方法を模索中です。 =head3 Moose 1.0はいつ出ますか Mooseはもう使えます! スティーヴン・リトル(Stevan Little)は2007年3月にリリースした0.18で「実用できるようになった」ことを宣言しました。 =head2 コンストラクタ =head3 Mooseで独自のコンストラクタを書くにはどうすればよいですか 理想的には、決して独自のCメソッドは書かないようにしてください(オブジェクト生成時に特別な処理が必要な場合は、Mooseのほかの機能を使って対処してください)。ここではいくつかのシナリオと、Mooseではどのように解決するかを紹介します。 インスタンス生成後に初期化コードを呼ぶ必要がある場合はCメソッドを使ってください(この機能はPerl 6から直接持ってきたものです)。インスタンスを生成すると、すぐにインスタンスチェーンにあるすべてのCメソッドが(正しい順序で)呼ばれるので、スーパークラスもすべて適切に、また確実に初期化することができます。これはクラスのサブクラス化が非常に簡単になりますので、(可能なときは)最善のアプローチです。 インスタンスが実際に生成される前にコンストラクタのパラメータをいじる必要がある場合は、いくつかのオプションがあります。 パラメータ処理をまるごと変更したい場合は、Cメソッドを利用できます。デフォルトの実装ではキー/値のペアか、ハッシュリファレンスを受け取るようになっていますが、オーバーライドすれば、順番通りに引数を受け取ったり、別のフォーマットの引数を受け取ったりできるようになります。 個々のパラメータの扱いを変える場合は「型変換」があります(型変換の完全な例や説明についてはLをご覧ください)。型変換を利用すると引数の値を期待された通りの型に変換できます。このアプローチがもっとも柔軟で頑丈です(学習曲線はややきつくなってしまいますが)。 =head3 Mooseを使っていないコンストラクタをMooseで利用するにはどうすればよいですか ふつうMooseを使っていないクラスをサブクラス化するときの正しいアプローチは委譲することです(これはCキーワードや型変換、Cを使えば簡単にできます)。だから、サブクラス化するのは往々にして理想的なやり方ではありません。 とはいえ、本当にMooseを使っていないクラスを継承する必要があるなら、Lにやり方が載っています。あるいはCPANにあるLをご覧ください。 =head2 アクセサ =head3 Mooseにgetアクセサとsetアクセサを使うよう指示するにはどうすればよいですか もっとも簡単なのはCとCというアトリビュートオプションを使う方法です。 has 'bar' => ( isa => 'Baz', reader => 'get_bar', writer => 'set_bar', ); これらのメソッドを生成しても型制約やトリガなどは利用できます。 こんなにたくさんタイプしたくないし、このようなget/setアクセサをデフォルトにしたいという方は、Lをご覧ください。このモジュールを使うとこのように書けるようになります。 has 'bar' => ( isa => 'Baz', is => 'rw', ); これで、MooseはCという単一のメソッドを作るかわりに、CとCという別々のメソッドを生成するようになります。 CとCにしたい場合はLをご覧ください。 注意:これをグローバルに設定することはB<できません>。そうしてしまうとMooseで作られたほかのクラスを壊してしまうからです。ただし、新たにMooseのシュガー関数をエクスポートするLを定義してLを有効にすればタイプ数を節約できます。Lをご覧ください。 =head3 アクセサの中で値をオブジェクトにしたり、その逆を行うにはどうすればよいですか まず、最初に確認したいのは、本当に値からオブジェクトへの変換とオブジェクトから値への変換の双方が必要なのか、ということ。 値からオブジェクトに変換できればよいのであれば、型変換を使うことをおすすめします。Lオブジェクトに変換する基本的なサンプルコードはこうなります。 class_type 'DateTime'; coerce 'DateTime' => from 'Str' => via { DateTime::Format::MySQL->parse_datetime($_) }; has 'timestamp' => (is => 'rw', isa => 'DateTime', coerce => 1); ここではLオブジェクト用に独自のサブタイプを作成して、そのサブタイプに型変換を追加しています。Cアトリビュートが期待しているのはC型の値ですが、型変換も試すよう指示されているので、CアクセサにC型の値が渡されると、Cブロックにあるコードを使ってCオブジェクトへの型変換を行おうとします。 より包括的な型変換の例についてはLをご覧ください。 アトリビュートに渡されたオブジェクトを値に変換する必要がある場合、いまのところベストプラクティスはアクセサにCモディファイアを追加することです。 # a timestamp which stores as # seconds from the epoch has 'timestamp' => (is => 'rw', isa => 'Int'); around 'timestamp' => sub { my $next = shift; my $self = shift; return $self->$next unless @_; # assume we get a DateTime object ... my $timestamp = shift; return $self->$next( $timestamp->epoch ); }; 型変換を使ってオブジェクトを値に変換することもできるのですが、これは概して非常に複雑で、多くのサブタイプがを必要とするものになります。こちらの例はこのドキュメントで扱う範囲を越えていますので、#mooseでたずねるか、メーリングリストに投稿してください。 さらにもうひとつ、独自のアトリビュートメタクラスを書くというオプションがあります。これもこのドキュメントで扱う範囲を超えていますが、#mooseやメーリングリストでなら喜んで説明します。 =head3 アトリビュートは作ったけれど、アクセサはどこにあるの アクセサは何もしなくても作られるものではB<ありません>。Mooseにアクセサを作るよう指示するB<必要があるのです>。おそらくこんなコードになっているのではありませんか。 has 'foo' => (isa => 'Bar'); 本当はこうしたかったのでしょう。 has 'foo' => (isa => 'Bar', is => 'rw'); このようになっている理由は、アクセサが「なくても」使い方としてはまったく問題ないからです。もっとも単純なのは、自前のアクセサを書きたい場合です。Mooseが自動的にアクセサを作ってしまうと、クラスの生成順序の都合で、自前のアクセサが上書きされてしまうのです。それでは困りますよね。 =head2 メソッドモディファイア =head3 Cを使ってC<@_>の値を変更するにはどうすればよいですか 実はできないのです。Cが実行されるのはメインメソッドの前だけなので、メソッド本体の実行にはおいそれと影響を与えられないのです。 同様に、Cを使ってメソッドの返り値に影響を与えることもできません。 CとCに制限を加えているのは、もっと簡潔なコードを書けるようにしたいからです(こうしておけば、もとのメソッドにC<@_>を渡したり、返り値を(コンテクストを保つよう気をつけながら)転送することを心配する必要がなくなります)。 Cメソッドモディファイアを使えばこのような制限はありませんが、少し冗長になります。 =head3 Cを使ってメソッドの実行を中断できますか できますが、止められるのは例外を発生させたときだけです。それでは過激すぎるということであれば、かわりにCを使うことをおすすめします(メインメソッドの実行を余裕を持って中断できるのはCメソッドモディファイアだけです)。例はこうなります。 around 'baz' => sub { my $next = shift; my ($self, %options) = @_; unless ($options->{bar} eq 'foo') { return 'bar'; } $self->$next(%options); }; C<$next>メソッドを呼ばないようにすれば、メインメソッドの実行を中断できます。 =head3 Cモディファイアの中で返り値が見られないのはなぜですか Cモディファイアと同様に、Cモディファイアも単にメインメソッドの「あとに」呼ばれるだけです。渡されるのはもともとのC<@_>の中身であって、メインメソッドの返り値ではB<ありません>。 これもなぜこうなっているかの議論は長すぎるので割愛しますが、Cの場合と同じく、Cモディファイアを使うことをおすすめします。サンプルコードはこのようになります。 around 'foo' => sub { my $next = shift; my ($self, @args) = @_; my @rv = $next->($self, @args); # do something silly with the return values return reverse @rv; }; =head2 型制約 =head3 型制約に独自のエラーメッセージを用意するにはどうすればよいですか サブタイプを作成するときにCオプションを利用してください。 subtype 'NaturalLessThanTen' => as 'Natural' => where { $_ < 10 } => message { "This number ($_) is not less than ten!" }; 値がCの型チェックに失敗すると、このCブロックが呼ばれます。 =head3 型制約のチェックを無効にすることはできますか まだできませんが、将来のリリースではこのオプションが入るかもしれません。 =head2 ロール =head3 合成したロールでBUILDが呼ばれないのはなぜですか BUILDは合成したロールでは決して呼ばれません。そのおもな理由は、ロールは順番の影響をB<受けない>からです(ロールは合成の順序が影響しないような形で合成されます。詳しい理論はLにある大本の論文をご覧ください)。 ロールには本質的に順序がないため、BUILDメソッドを実行する順序を決めることはできません。 ただし、それ以外の解決策はいくつかあります。 =over 4 =item * アトリビュートに遅延評価とデフォルト値を組み合わせておくと、初期化を遅らせることができます(遅延評価やデフォルトの使い方についてはクックブックのLにあるバイナリ木の例が好例なのでご覧ください)。 =item * アトリビュートのトリガを使うと(これはアトリビュートの値が設定されたときに実行されます)初期化を簡単にできます。これについてはLのドキュメントに説明されているほか、テストスイートに例があります。 =back 一般的に、ロールは初期化をB<必要>とすべきではありません。まともなデフォルト値を用意しておくか、具体的に初期化が必要であるとドキュメント化しておくべきです。「ドキュメント化」のひとつの方法としては、アトリビュートの初期化メソッドを別に用意して、ロールの必須メソッドとすることです。例としてはこのようになります。 package My::Role; use Moose::Role; has 'height' => ( is => 'rw', isa => 'Int', lazy => 1, default => sub { my $self = shift; $self->init_height; } ); requires 'init_height'; この場合、このロールはCメソッドを用意しているクラスにしか合成できません。 このような解決策でもうまくいかない場合、ロールは問題を解決するのに最適なツールではないのかもしれません(本当にクラスを使う必要があるのかもしれません)。少なくとも、問題のロールの機能を削って、初期化を必要としないようにした方がよいでしょう。 =head3 トレートとはなんですか。ロールとはどう違うのですか Mooseのトレートはロールとほとんど同じものです。ただし、トレートの方は通常登録されているので(「MyApp::Role::Big」を「Big」という風に)短い名前で呼ぶことができます。 Mooseの文脈では、「ロール」はふつうコンパイル時に「クラス」に合成されます。一方の「トレート」は、ふつうは実行時にクラスのインスタンスに合成して、B<そのインスタンスだけ>に振る舞いを追加したり修正を加えたりするものです。 Mooseの文脈を離れると、トレートとロールは一般的にはまったく同じ意味です。元々の論文ではトレートと呼ばれていましたが、Perl 6ではロールと呼ぶことになっています。 =head2 Mooseとサブルーチンアトリビュート =head3 スーパークラスから継承したサブルーチンアトリビュートが動かないのはなぜですか いまのところCキーワードを使ったモジュールのサブクラス化は実行時に行われますが、アトリビュートはコンパイル時にチェックされるため、アトリビュートを有効にするにはCをCブロックの中に入れて、コンパイル時にアトリビュートハンドラを利用できるようにしなければなりません。 BEGIN { extends qw/Foo/ } 念のため、ここで話題にしているのはPerlのサブルーチンアトリビュートについてです。Mooseのアトリビュートについてではありません。 sub foo : Bar(27) { ... } =head1 作者 Stevan Little Estevan@iinteractive.comE =head1 COPYRIGHT AND LICENSE Copyright 2006-2009 by Infinity Interactive, Inc. L This library is free software; you can redistribute it and/or modify it under the same terms as Perl itself. =cut