北海道苫小牧市出身の初老PGが書くブログ

永遠のプログラマを夢見る、苫小牧市出身のおじさんのちらしの裏

Moose::Roleのメソッドの競合(2) - 蛇足編

かなり蛇足ですが、昨日のエントリに追記。

「メソッドが競合している」ではなく、「クラス側にメソッドを実装しなさい」と怒られる

Traitsの形式的定義によれば、

clashes, i.e., names that are defined in both T1 and T2 , annihilate each other and do not appear in the resulting record.

となってますので、重複したメソッドは削除されて合成されることを想定しているようです。じゃあ、なんでこんなエラーになるのかとソースを読むと、
Moose::Meta::Role::Application::RoleSummation にて・・・

    foreach my $method (@all_methods) {
        if (exists $seen{$method->{name}}) {
            if ($seen{$method->{name}}->body != $method->{method}->body) {
                $c->add_required_methods($method->{name});   # ← required に含めてる!
                delete $method_map{$method->{name}};         # ← こっちはTraits的な仕様
                next;
            }           
        }       
        
        $seen{$method->{name}}       = $method->{method};
        $method_map{$method->{name}} = $method->{method};
    }

重複したメソッドをちゃっかりadd_required_methodsしてますね。これを消すと、警告も何も言われなくなります。←追記参照、消しちゃ駄目!(笑

テスト側を見てみると、

... role methods conflicted and method was required

とテスト名に書いているので、なんとなく「role method」と「(class) method」を分けて考えてるっぽい?? つまりはRoleメソッドじゃなくて(クラスの)メソッドを実装してねーって言う意味ともとれます。

まあ、この辺は理由があってこのような実装に落ち着いてる*1と思うので、Traitsの概念はともかく、Moose::Roleはこういう動作をする物だと思った方がいいのかもしれません。←追記参照

4/7 追記

今読み直すと、P.12の

Traits enforce explicit resolution of conflicts by excluding the conflicting methods and therefore turning them into required methods.

がこの仕様のような気がします。Moose::RoleはやはりTraitsに沿ってるっぽいですね。

ただし、その後の exclusion の解説である、

In addition to conflict resolution, trait composition also supports exclusion,
...
This suppresses these methods and turns them into requirements,
...

は実装されてないみたいです。以下はエラーになりません。

BEGIN {
	package TraitsA;
	use Moose::Role;
	sub hoge{}
	no  Moose::Role;

	package ClassA;
	use Moose;
	with 'TraitsA' => {
		excludes => ['hoge']
	};
	no  Moose;
}

ClassA->new;

4/7 追記(2)

はてブへのレスです。

excludes(@methods) じゃなくてexcludes(@roles)ではないかな

excludesには二つあります。

ここで言及してるのはwithと共に使う Roles/Recipe2.podSpec/Role.podに出て来る機能で、合成しないメソッドを指定するものです。ちなみに、「実装されてないみたいです」と書いたのは、『Traitsの論文にあるexcludesの「This suppresses these methods 」の機能は実装されている*2が、「and turns them into requirements,」に相当する振る舞いはしない』、と言う意味でした。

一方のRoleを指定するexcludesはRole.pmManual/Roles.podに出てきますが、こちらはTraitsの論文とは別の種類のもののようです*3

*1:実際問題、競合したメソッドを勝手に消されると非常に困りますし。

*2:先ほどのコードで ClassA->new->hoge はできません。

*3:Fortressと言う言語の言語仕様のようです。