NAME

mro - メソッド解決順序(Method Resolution Order)


SYNOPSIS

  use mro; # enables next::method and friends globally
  use mro 'dfs'; # enable DFS MRO for this class (Perl default)
  use mro 'c3'; # enable C3 MRO for this class


DESCRIPTION

"mro" 名前空間はメソッド解決順序と一般的なメソッドキャッシュを扱うための いくつかのユーティリティを提供します。

これらのインターフェースは Perl 5.9.5 以上でのみ利用可能です。 より古い Perl のための、ほとんど前方互換な実装については CPAN の MRO::Compat を参照してください。


概観

あるクラスの MRO は、上述の use mro を使うか、以下の mro::set_mro 関数を使うことで変更できます。 mro 名前空間の関数は mro モジュールを読み込む必要はなく、実際には コア perl インタプリタによって提供されます。

特殊メソッド next::method, next::can, maybe::next::methoduserequire によって mro モジュールを読み込むまで 利用できません。


The C3 MRO

伝統的な Perl のデフォルトの MRO (深さ優先探索; ここでは DFS と 呼ばれます)にくわえて、Perl は C3 MRO も提供するようになりました。 Perl の C3 対応は Stevan Little による Class::C3 モジュールで行われた 作業を基としていて、ここにある C3 関係の文章のほとんどは、そこから直接 コピーされたものです。

C3 って何?

C3 は多重継承における健全なメソッド解決順序を提供することを目的とした アルゴリズムの名前です。 これは最初に Dylan と言う言語 (SEE ALSO の章のリンクを 参照してください)、で導入され、後に Python 2.3 の新型のクラスでの 優先 MRO (Method Resolution Order; メソッド解決順序) として採用されました。 つい最近では Perl 6 のクラスでの「正統な」MRO として採用され、Parrot プロジェクトでもデフォルトの MRO として採用されました。

C3 の動作

C3 は常に局所的な優先順位を保存して動作します。 これは、基本的にどのクラスもそのサブクラスより先に現れることはないことを 意味します。 例えば、以下のような古典的なダイヤ型継承パターンを考えます:

     <A>
    /   \
  <B>   <C>
    \   /
     <D>

標準の Perl 5 MRO は (D, B, A, C) です。 この結果、CA のサブクラスにも関わらず、AC より先に 検索されます。 しかし、C3 MRO アルゴリズムでは、(D, B, C, A) の順序になり、この問題は ありません。

この例はかなりつまらないものです; より複雑な場合とより深い説明については、 SEE ALSO の章のリンクを参照してください。


関数

mro::get_linear_isa($classname[, $type])

与えられたクラスの、線形化された MRO を含む配列リファレンスを返します。 Uses whichever MRO is currently in effect for that class by default, or the given MRO (either c3 or dfs if specified as $type). (TBT)

あるクラスの、線形化された MRO とは、そのクラスでメソッドを解決するときに 検索する(自分自身のクラスを先頭とする)全てのクラスの順序付き配列です。

要求されたクラスがまだ存在していない場合、この関数はそれでも成功し、 [ $classname ] を返します。

UNIVERSAL (および UNIVERSAL の MRO のメンバ) は、あるクラスの MRO の一部ではないことに注意してください; 全てのクラスは暗黙に UNIVERSAL とその親を継承しているにも関わらず、です。

mro::set_mro($classname, $type)

与えられたクラスの MRO を $type 引数 (c3dfs のどちらか) に 設定します。

mro::get_mro($classname)

与えられたクラスの MRO (c3dfs のどちらか) を返します。

mro::get_isarev($classname)

このクラスの mro_isarev を取得して、クラス名の配列リファレンスとして 返します。 These are every class that "isa" the given class name, even if the isa relationship is indirect. This is used internally by the MRO code to keep track of method/MRO cache invalidations. (TBT)

現在のところ、このリストは伸びるだけで、縮むことはありません。 This was a performance consideration (properly tracking and deleting isarev entries when someone removes an entry from an @ISA is costly, and it doesn't happen often anyways). The fact that a class which no longer truly "isa" this class at runtime remains on the list should be considered a quirky implementation detail which is subject to future change. It shouldn't be an issue as long as you're looking at this list for the same reasons the core code does: as a performance optimization over having to search every class in existence. (TBT)

上述の mro::get_mro と同様、UNIVERSAL は特殊です。 UNIVERSAL (と親) の isarev リストは存在する全てのクラスを 含んでいるわけではありません; メソッド継承の目的において 全てのクラスが事実上子孫であるにも関わらず、です。

mro::is_universal($classname)

与えられたクラス名が、UNIVERSAL 自身あるいは @ISA 継承による UNIVERSAL の親の一つかどうかを示す真偽値を返します。

この関数が真を返すあらゆるクラスは、全てのクラスが潜在的にここから メソッドを継承しているという意味で "universal" です。

上述の isarev と同様の理由で、このフラグは恒久的です。 一旦これがセットされると、例え問い合わされたクラスが実際には もはや univeral ではないとしても、セットされたままです。

mro::invalidate_all_method_caches()

PL_sub_generation をインクリメントして、全てのパッケージの メソッドキャッシュを無効にします。

mro::method_changed_in($classname)

与えられたクラスに依存している全てのクラスのメソッドキャッシュを 無効にします。 通常これは不要です。 ピュア perl コードがメソッドキャッシュについて混乱すると知られている 唯一の場合は、constant の内部で行っているように、読み込み専用の スカラ値を使って新しい定数サブルーチンを手動で設定した場合です。 もしその他の場合を発見した場合は、どうか報告してください; それを修正するか、ここに例外として記述します。

mro::get_pkg_gen($classname)

Returns an integer which is incremented every time a real local method in the package $classname changes, or the local @ISA of $classname is modified. (TBT)

This is intended for authors of modules which do lots of class introspection, as it allows them to very quickly check if anything important about the local properties of a given class have changed since the last time they looked. It does not increment on method/@ISA changes in superclasses. (TBT)

It's still up to you to seek out the actual changes, and there might not actually be any. Perhaps all of the changes since you last checked cancelled each other out and left the package in the state it was in before. (TBT)

This integer normally starts off at a value of 1 when a package stash is instantiated. Calling it on packages whose stashes do not exist at all will return 0. If a package stash is completely deleted (not a normal occurence, but it can happen if someone does something like undef %PkgName::), the number will be reset to either 0 or 1, depending on how completely package was wiped out. (TBT)

next::method

これはいくらか SUPER と似ていますが、多重継承の場合によりよい 一貫性を保つために C3 メソッド解決順序を使います。 一般的な継承はそのクラスに対してどの MRO が有効かに従うのに対して、 next::method は C3 MRO だけを使うことに注意してください。

一つの一般的な使用法は以下のようなものです:

  sub some_method {
    my $self = shift;
    my $superclass_answer = $self->next::method(@_);
    return $superclass_answer + 1;
  }

メソッド名を(再)指定しないことに注意してください。 常に開始したメソッドと同じメソッド名を使うことを強制されます。

これはもちろんオブジェクトやクラスを呼び出せます。

呼び出す実際のメソッドを解決する方法は:

  1. まず、呼び出されたオブジェクトやクラスの線形化された C3 MRO を決定します。

  2. 次に、起動されたコンテキストのクラスとメソッド名を決定します。

  3. 最後に、it searches down the C3 MRO list until it reaches the contextually enclosing class, then searches further down the MRO list for the next method with the same name as the contextually enclosing method. (TBT)

次のメソッドを検索するのに失敗すると、例外が投げられます (代替案については以下を参照してください)。

This is substantially different than the behavior of SUPER under complex multiple inheritance. (This becomes obvious when one realizes that the common superclasses in the C3 linearizations of a given class and one of its parents will not always be ordered the same for both.) (TBT)

Caveat: Calling next::method from methods defined outside the class: (TBT)

There is an edge case when using next::method from within a subroutine which was created in a different module than the one it is called from. It sounds complicated, but it really isn't. Here is an example which will not work correctly: (TBT)

  *Foo::foo = sub { (shift)->next::method(@_) };

The problem exists because the anonymous subroutine being assigned to the *Foo::foo glob will show up in the call stack as being called __ANON__ and not foo as you might expect. Since next::method uses caller to find the name of the method it was called in, it will fail in this case. (TBT)

But fear not, there's a simple solution. The module Sub::Name will reach into the perl internals and assign a name to an anonymous subroutine for you. Simply do this: (TBT)

  use Sub::Name 'subname';
  *Foo::foo = subname 'Foo::foo' => sub { (shift)->next::method(@_) };

and things will Just Work. (TBT)

next::can

これは next::method と同様ですが、単にコードリファレンスを返します; この名前のメソッドがもうない場合は undef を返します。

maybe::next::method

単純な場合では、これは以下と等価です:

   $self->next::method(@_) if $self->next_can;

しかし、(goto &maybe::next::method のように)この方法のみが動作する 場合もあります;


SEE ALSO

The original Dylan paper

http://www.webcom.com/haahr/dylan/linearization-oopsla96.html

The prototype Perl 6 Object Model uses C3

http://svn.openfoundry.org/pugs/perl5/Perl6-MetaModel/

Parrot now uses C3

http://aspn.activestate.com/ASPN/Mail/Message/perl6-internals/2746631
http://use.perl.org/~autrijus/journal/25768

Python 2.3 MRO related links

http://www.python.org/2.3/mro.html
http://www.python.org/2.2.2/descrintro.html#mro

C3 for TinyCLOS

http://www.call-with-current-continuation.org/eggs/c3.html

Class::C3

Class::C3


AUTHOR

Brandon L. Black, <blblack@gmail.com>

Based on Stevan Little's Class::C3