Perl の Class::Accessor::Lvalue::Fast モジュールの使い方とか実装とか
Class::Accessor::Lvalue::Fast モジュールを使っているコードをしばしば業務で目にしつつも、ちゃんと体系だって理解してなかったので調べてみた。 自分用メモ程度の内容なので詳細はドキュメント参照ということで
Class::Accessor 系のモジュール
Class::Accessor::Lvalue::Fast クラスは、Class::Accessor クラスを拡張するもの。 ここでは、Class::Accessor クラスとそれを拡張するようなクラス群を Class::Accessor 系のクラスと呼ぶことにして、それらのクラスを提供するモジュールについて説明する (Class::Accessor::Lvalue::Fast モジュールに関連するもののみ)。
Class::Accessor モジュール
基本となるのは Class::Accessor クラス。 このクラスを親クラスとするサブクラスを作ると、サブクラスには Class::Accessor クラスから以下のものが提供される。
- ハッシュリファレンスベースのインスタンスを生成するクラスおよびインスタンスメソッド new
- インスタンスメソッド get, set
- アクセサメソッドを作るためのクラスメソッド mk_accessors, mk_ro_accessors, mk_wo_accessors
new メソッドはハッシュリファレンスを引数に取ることができ、ハッシュリファレンスがある場合はそのコピーがインスタンスのベースになる。 引数がない場合は、空ハッシュのリファレンスをベースにインスタンスが生成される。
インスタンスメソッド set は、指定されたキーを使ってインスタンスのハッシュから値を取り出し、それを返す。 インスタンスメソッド get は、指定されたキーを使ってインスタンスのハッシュに値を格納する。
mk_accessor, mk_ro_accessors, mk_wo_accessors はクラスメソッドであり、このメソッドを実行することでアクセサメソッドとなるインスタンスメソッドが定義される。 引数として、生成されるアクセサメソッドの名前となる文字列のリストを取る。 さらにその名前がインスタンスのハッシュのキーにもなる。 mk_ro_accessors は読み取り専用のアクセサ、mk_wo_accessors は書き込み専用のアクセサを定義する。 定義されるアクセサは以下の形式になる。
$self->accessor_name( $key, @values )
mk_accessor メソッドの引数に渡した文字列の値が accessor_name になる。 @values が空の場合 getter となり、@values が 1 つ以上の値を持つ場合は setter になる。 @values として 2 つ以上の値を渡した場合は、それらの値を要素としてもつ配列へのリファレンスが値としてセットされる。 なお、このアクセサメソッドは make_accessor, make_ro_accessor, make_wo_accessor メソッドによって生成されるものであり、これらのメソッドは単に get または set メソッドを呼び出すようなメソッドを生成する。
サブクラスで set, get メソッドや make_accessor, make_ro_accessor, make_wo_accessor メソッドをオーバーライドすることで、定義されるアクセサメソッドの動作を変更することができる。
Class::Accessor::Fast モジュール
Class::Accessor::Fast クラスは Class::Accessor クラスのサブクラス。 make_accessor, make_ro_accessor, make_wo_accessor メソッドをオーバーライドし、get, set メソッドを使わずに、直接インスタンスのハッシュの値を読み書きするようなアクセサを生成する。
アクセサの動作が多少速くなるが、get, set メソッドのオーバーライドによる動作の変更ができなくなるという欠点もある。
Class::Accessor::Lvalue モジュール
Class::Accessor::Lvalue クラスは Class::Accessor クラスのサブクラス。 make_accessor, make_ro_accessor, make_wo_accessor メソッドをオーバーライドし、アクセサメソッドに代入できるような文を書けるようになる。
# __PACKAGE__->mk_accessor( 'accessor' ) で作られたアクセサメソッドに対して以下のように代入の式を書ける $obj->accessor = 120;
実装としては、アクセサメソッドを lvalue サブルーチンにしている。 get, set メソッドを使うために tie 関数を使っているので動作が遅くなっているようで、get, set メソッドを使う必要がない場合は Class::Accessor::Lvalue::Fast を使うことを公式的に薦めている。 tie 関数や lvalue サブルーチンに関しては後述。
Class::Accessor::Lvalue::Fast モジュール
Class::Accessor::Lvalue::Fast クラスは Class::Accessor::Fast クラスのサブクラス。 Class::Accessor::Lvalue クラスと同様に make_accessor, make_ro_accessor, make_wo_accessor メソッドをオーバーライドし、アクセサメソッドに代入できるような文を書けるようになる。 ただし、Class::Accessor::Fast クラスと同様に get, set メソッドを介さずに直接インスタンスのハッシュを操作するアクセサメソッドが定義されるので、Class::Accessor::Lvalue クラスを使った場合よりもアクセサメソッドの動作が高速になる。
内部実装の話
内部実装で使われているものたち。
lvalue サブルーチン
サブルーチンの定義時に lvalue 属性をつけてやることで、(呼び出し側から見ると) メソッドへの代入ができるようになる、というもの。 Perl 5.6.0 からサポートされてるらしい。 便利っぽいけど下記のように書かれているので、あまりがしがし使わないほうがいいのかも。
Lvalue subroutines are still experimental and the implementation may change in future versions of Perl.
Lvalue subroutines (perlsub - perldoc.perl.org)
なお、Want モジュール を使うことで、lvalue 属性がつけられたメソッドへの代入としての呼び出しがされたのかそうでないのかを知ることができる。
tie 関数
- tie - perldoc.perl.org
- 洗練されたPerl: タイ変数 (Tied variables)
- tie関数とは - 燈明日記
- perl - tie()って何をtieするの? (404 Blog Not Found)
tie()とはなにか?
ずばり、「オブジェクトではない、perlの組み込みデータタイプを裏でオブジェクト化する仕組み」のことです。それがなぜtieと呼ばれるかと言えば、変数を指定されたクラスに結びつけるから、です。
perl - tie()って何をtieするの? (404 Blog Not Found)
Class::Accessor::Lvalue モジュールでは、lvalue サブルーチンの戻り値として tie された変数を返すことで、アクセサメソッドを lvalue サブルーチンにしつつ、get, set メソッドを使ってハッシュを操作するということを実現している。