=encoding utf8 =pod =head1 題名 Moose::Spec::Role - ロールの振る舞いに関する公式仕様 =head1 本文 B<注意:> このドキュメントには現在不備があります。 =head2 ロールの構成要素 =over 4 =item 排除ロール ロールには排除するロールの一覧が定義されていることがあります。これは基本的に合成できないロールのことです。これは直接合成できないものだけでなく、「継承」したときに合成できないものも含まれます。 この機能はFortress言語をまねたものですが、大量のロールを「ブロック」のように組み合わせていくときには本当に役に立ちます。「ブロック」の中にはどうしても組み合わせられないものもあるからです。 =item アトリビュート ロールのアトリビュートはクラスのアトリビュートと似ていますが、実際にロールに適用されるわけではないところが異なります。つまり、アトリビュートアクセサが生成するメソッドは、そのロールの中で生成されるのではなく、ロールがなにかのクラスに組み込まれてから作成される、ということです。 =item メソッド ロールの中で定義されるメソッドは次の通りです。簡単ですよね? =item 必須メソッド ロールは、取り込む側のクラス(ないしロール)が特定のメソッドを提供していることを要求することがあります。クラスがそのメソッドを提供していない場合は致命的なエラーになります。組み込み先がロールの場合は、メソッドの要件をそのロールに引き継ぎます(致命的なエラーにはなりません)。 =item 必須アトリビュート ロールは、特定のメソッドを必要とすることがあるのと同様に、特定のアトリビュートを必要とすることもあります。この場合、対象となるアトリビュートは、最低でも必要とされる要件を満たしている必要があります。つまり、たとえばあるロールが読み取り専用のアトリビュートを要求している場合、そのアトリビュートは少なくとも読み取り用のアクセサを持っている必要があります(ただし、書き込み用のミューテータを持っていてもかまいません)。ロールが配列リファレンス型のアトリビュートを要求している場合、そのアトリビュートの型は配列リファレンスあるいはそのサブタイプでなければなりません。 =item オーバーライドされるメソッド CとCはロールの中でも使えますが、その挙動はクラスの場合とは異なります。クラスのCは直接そのクラスのスーパークラスを指しますが、ロールのCは遅延評価され、クラスに合成されるまで意味を持ちません。合成されてからは、Cはそのクラスのスーパークラスを指すようになります。 ロールは階層構造を持たないということはぜひ覚えておいてください。だからこそ、「スーパー」ロールを持つことはありえないのです。 =item メソッドモディファイア Mooseのクラスが提供しているCやC、Cといったモディファイアも利用できますが、こちらも実際にロールがクラスに合成されるまでは適用されないという違いがあります(アトリビュートやCの場合と同じです)。 =back =head2 ロールの組み込み =head3 クラスに組み込む場合 =over 4 =item 排除ロール =item 必須メソッド =item 必須アトリビュート =item アトリビュート =item メソッド =item オーバーライドされるメソッド =item メソッドモディファイア(before、around、after) =back =head3 インスタンスに組み込む場合 =head3 ロールに組み込む場合 =over 4 =item 排除ロール =item 必須メソッド =item 必須アトリビュート =item アトリビュート =item メソッド =item オーバーライドされるメソッド =item メソッドモディファイア(before、around、after) =back =head3 ロールの合成 あるロールに(Cを利用して)複数のロールを組み込んだ場合は対称差を利用して合成されます。このようにしてできたロールを合成ロールといいます(L)。 =over 4 =item 排除ロール =item 必須メソッド =item 必須アトリビュート =item アトリビュート 同じ名前を持つアトリビュートが複数ある場合は衝突を起こして回復不能なエラーとみなされます。アトリビュートのほかの要素は考慮されません。アトリビュート名が衝突しているだけで十分です。 このようにアトリビュートの衝突判定が非常に早く、厳しいものになっているのは、アトリビュートは変動の幅が大きいのですぐに収拾がつかなくなってしまうためです。また、判定のルールも非常に煩雑なものになってしまいますし、そのような手間をかけるだけの価値はないと筆者は考えています。 =item メソッド 同じ名前を持つメソッドが複数ある場合は衝突を起こしますが、エラーにはなりません。そのかわりにそのメソッド名が新しくできた合成ロールの「必須」メソッドのリストに追加されます。 これを集合論の観点から見ると、それぞれのロールはメソッドの集合を持つことができると言えます。そして、この2つの集合の対称差が新たに合成ロールのメソッドの集合になり、2つの集合の積集合が衝突になります。例を示すと、このようになります。 Role A has method set { a, b, c } Role B has method set { c, d, e } The composite role (A,B) has method set { a, b, d, e } conflict set { c } =item オーバーライドされるメソッド オーバーライドされたメソッドが衝突を起こす場合は2通りあります。 まず、同じ名前を持つ別のオーバーライドされたメソッドがある場合。これは回復不能なエラーとみなされます。これがエラーになるのは自明でしょう。同じクラスの中でひとつのメソッドを2度オーバーライドすることはできません。 2つめは、オーバーライドされたメソッドと通常のメソッドが同じ名前を持つ場合です。これも回復不能なエラーです。この2つのメソッドを合成する方法はありませんし、両者をどこかでひとつのクラスに合成することもできないからです。 ロールの中でオーバーライドするのはトリッキーですが、気をつけて使えば非常に強力なツールになることもあります。 =item メソッドモディファイア(before、around、after) メソッドモディファイアだけはロールを合成するときでも順序を気にしなければなりません。これはメソッドモディファイアそのものの性質のためです。 メソッドは複数のメソッドモディファイアを持つことがあるため、ロールを組み込む段階ではモディファイアは適用されません。クラスに組み込んだときに同じ順番で適用できるよう収集されるだけです。 一般論として、ロールの中でメソッドモディファイアを使うときは細心の注意を払ってください。このように順序の問題があるため、使いすぎると微妙で見つけづらいバグのもとになることもあります。人生なにごとも節度が肝心です。 =back =head3 特殊な組み込み例 ここでは混乱しやすい複雑な特殊をいくつか紹介します。これは問題を明確にして、中でなにが起こっているを説明しようというものです。 =over 4 =item ロールメソッドのオーバーライド 取り込もうとしているロールのメソッドを「オーバーライド」したがる人は多いのですが、これは相手がクラスであればうまくいくものの(ローカルなクラスメソッドの方がロールのメソッドより優先されるためです)、ロールが相手の場合はかなりトリッキーなことになります。メソッドの衝突が起こると、どちらのメソッドも組み込まれず、「必須」メソッドになってしまうのです。 この(正しくない)オーバーライドの例をあげます。 package Role::Foo; use Moose::Role; sub foo { ... } package Role::FooBar; use Moose::Role; with 'Role::Foo'; sub foo { ... } sub bar { ... } この場合、Cメソッドが衝突を起こしていますので、Role::FooBarは取り込む側のクラスやロールにCの実装を要求します。これはたいていの場合期待した動作ではないはずです。 こちらは(正しい)オーバーライドの例です。もっとも、これも次に説明するように、オーバーライドはいっさい起こっていません。 package Role::Foo; use Moose::Role; sub foo { ... } package Role::Bar; use Moose::Role; sub foo { ... } sub bar { ... } package Role::FooBar; use Moose::Role; with 'Role::Foo', 'Role::Bar'; sub foo { ... } これが動作するのは、Role::FooとRole::Barを合成するとCが衝突するため、Cが(Iを使って合成されたRole::FooとRole::Barの)合成ロールの必須メソッドになり、その要件をRole::FooBarが満たすからです。 大事なのは、Role::FooBarは単にCメソッドの要件を満たしているだけで、Cをオーバーライドしている「わけではない」ということ。これは大きな違いになります。 さらにもうひとつ(正しい)オーバーライドの例を見ましょう。今度はIオプションを使います。 package Role::Foo; use Moose::Role; sub foo { ... } package Role::FooBar; use Moose::Role; with 'Role::Foo' => { excludes => 'foo' }; sub foo { ... } sub bar { ... } 合成するときにCメソッドを明示的に排除すると、Bに自前のCを定義できるようになります。 =back =head1 参照 =over 4 =item トレート(Traits) ロールはSmalltalkコミュニティで生まれたトレートが元になっています。 =over 4 =item L ここは元になったトレート論文のメインサイトです。 =item L 私が数年前に上でリンクした論文を読んでトレートを実装したものです(このモジュールは現在Ovidがメンテナンスしています。私はもう関与していません)。 =back =item ロール ロールという概念は比較的新しいものですし、おそらくMooseの実装がもっとも成熟しているので、あまりリンクするべきところもないのですが、多少なりとも見る価値があるものを紹介します(ほとんどはPerl 6関連のものです)。 =over 4 =item L これはchromaticによるロールの解説です。彼は中心的な旗振り役のひとりでした(です)から、一読の価値はあります。 =item L このPerl 6の概要第12章ではPerl 6のオブジェクトシステムの全貌が語られているのですが、もちろんここにはロールについての話も含まれています。 =back =back =head1 作者 Stevan Little Estevan@iinteractive.comE =head1 COPYRIGHT AND LICENSE Copyright 2007-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