メタメソッド

この記事はPerl6 Advent Calendar 2011の10日目です。


内容的には前回の「クラスとオブジェクト」の続きのような感じなので、この記事を読まれる前に、そちらを先に読む事をお勧めします。

さて、前回の記事ではPerl6のオブジェクトがメタクラスによって管理されている事を紹介しました。今回は、そのメタクラスのメソッド(メタメソッド)にどんなものがあるのかを紹介して行こうと思います。実用的なプログラミングだけでなく、デバッグや内部構造の勉強に非常に便利なので、ガンガン使っていきましょう!

methods

methodsメタメソッドは、そのオブジェクトが持っているメソッド(オブジェクト)の一覧を返します。オプションにlocalを指定する事で、そのクラスで定義されている(親クラスから受け継いだものではない)メソッドだけを表示する事ができます。このメタメソッドを使う事で組み込みのクラスのメソッドを簡単に調べられるので、「xxしたいんだけど、それっぽいメソッドないかなー」というときにとても便利です。

attributes

methodsのattributes版です。

find_method

引数に指定した名前のメソッドオブジェクトを返します(存在しない場合はMuが返る)。特定のメソッドのシグネチャや返り値を見る時に便利ですね。

add_method

クラスまたはオブジェクトに対して、メソッドを登録します。クラスに対してメソッドを追加した場合は、add_methodを呼ぶ前に生成されたインスタンスであっても、そのクラスのインスタンスは全てそのメソッドを持ちます。オブジェクトに対してメソッドを追加した場合は、Rubyの特異メソッドのように、そのオブジェクトのみに対してメソッドが追加されます。
これは私の思い違いでした。クラスに対して追加した場合も、オブジェクトに対して追加した場合も同じ挙動になるようです。検証が甘くて申し訳ありません…。


以下にadd_methodを使ってメソッドを追加する例を示します。


クラスに対してメソッドを追加する例

class C { }

my $c1 = C.new();
my $c2 = C.new();
C.^add_method("do_it", method () { say "Done!"});
$c1.do_it;
#> Done!
$c2.di_it;
#> Done!

オブジェクトに対してメソッドを追加する例

class C { }

my $c1 = C.new();
my $c2 = C.new();
$c1.^add_method("do_it", method () { say "Done!"});
$c1.do_it;
#> Done!
$c2.do_it;
#> Done!

ちなみにこのadd_method、メソッドを追加するにあたって色々なオプションがあります。詳しくはsrc/Perl6/metamodel/BOOTSTRAP.pm を参照してください。


(追記)
あるオブジェクトだけに特有のメソッドを持たせるには無名roleを使うしかないようです。

class C { }
my $c1 = C.new;
my $c2 = C.new;
$c1 does role { method do_it { say "Done!" } }
$c1.do_it;
$c2.do_it;

#>Done!
#>Method 'do_it' not found for invocant of class 'C'

ちなみに、無名roleを追加したオブジェクトの型を見ると、

C+{<anon>}()

という型が新たに作られているのが判ります。これについては誰かの別の記事で説明があるかもしれません(この記事では割愛します)。

add_attribute

add_methodのattribute版です。

parents

そのオブジェクトの親クラスの一覧を返します。引数にtreeオプションを渡す事で、フラットなリストではなく継承関係のツリーが得られます。

まとめ

他にもメタクラスが持つメソッドは幾つもありますが、普段使う上で便利なのはこのくらいでしょうか。こうやって手軽にオブジェクトの中身を見たり、書き換えたりできる所はPerl6の魅力の一つでもあります。