Posted at
PerlDay 3

その"attribute"はあなたの知ってる"attribute"じゃないかもしれない

More than 3 years have passed since last update.

突然ですが、Perl(特にオブジェクト指向の文脈)における"attribute"という単語の意味は 非常に曖昧です。 Perlモジュールのドキュメントを読む時に"attribute"という概念が説明なしに使われていた場合、読み手は最大限の警戒をしなければなりません。

大別すると、"attribute"には二つの意味があります。


  1. サブルーチンや変数にラベルのようにしてつける補助属性

  2. 「オブジェクト指向」的な意味でのオブジェクトが内部に持つデータ。 よその言語では「メンバ変数」や「フィールド」や「プロパティ」などとも呼ばれる。

多くのプログラマーがattributeという言葉から連想するのはおそらく2.ではないでしょうか。 しかしPerl関連の文書では1.の意味でも使われており、注意が必要です。


サブルーチンや変数の補助属性としてのattribute

Perlによく見られる「ちょっとマイナーな言語機能」の一つです。

例えば、サブルーチンに"lvalue" attributeを付与することで左辺値として使えるサブルーチンを定義できます。

use feature qw(say);

my $val = 0;

sub value : lvalue { # lvalue attribute
$val;
}

say value(); # => 0
say $val; # => 0

value() = 10; # サブルーチン呼び出しに代入!

say value(); # => 10
say $val; # => 10

lvalueの他にもいくつか組み込みのattributeがあるらしいし、変数にもattributeをつけることができるらしいですが、僕は使ったことがないのでよく分かりません。個人的には、どうしてもという理由がなければ使わない方がよい機能だと思っています。

参考:


オブジェクトの内部データとしてのattribute

オブジェクト指向な機能を持つプログラミング言語であればたいてい同様の概念があるので、こちらに関してはこれ以上特に注意すべき点はありません。

・・・と言いたいところですが、Perlのオブジェクト指向は しっちゃかめっちゃか 伸び伸びとした自由闊達な気風を持ち合わせているので、 こちらの意味だったとしてもまだ気は抜けません。

オブジェクトの内部データという意味でattributeという言葉が使われている場合、だいたい以下のいずれかまたは複数を暗に意味しています。


  1. オブジェクトはハッシュとしてアクセス可能であり、attributeはハッシュキーを意味している。

  2. attributeは、オブジェクトのコンストラクタ(new()メソッド)に名前付き引数として渡せるデータを意味している。

  3. attributeは、オブジェクトの内部データへのアクセサメソッドを意味している。

個人的な印象ですが、2 + 3のパターンが多い気がしています。


ハッシュキーとしてのattribute

この意味のattributeを使っているモジュールの代表格はDBIです。

use DBI;

my $dbh = DBI->connect("dbi:SQLite:dbname=hoge.sqlite3");
$dbh->{AutoInactiveDestroy} = 1;
$dbh->{PrintError} = 0;
$dbh->{RaiseError} = 1;

このように、DBIではユーザがハンドルオブジェクトをハッシュとして扱うことを許しており、そのハッシュキーを指してattributeという言葉を使っています。

昨今のオブジェクト指向Perlでは実装隠蔽の立場から言って(※)このようなやり方は望ましくないと一般的に考えられているため、このパターンのモジュールは比較的少数派だと思います。

※「オブジェクトにハッシュとしてアクセスできる」からといって「オブジェクトの実体がハッシュである」とは限らないのですが、いろいろややこしいテクニックが必要になります。


コンストラクタの名前付き引数としてのattribute

この意味でattributeを使っているクラスのコンストラクタは例えばこのように実装されています。

package Hoge;

sub new {
my ($class, %attributes) = @_;
return bless {
attr1 => "hoge",
attr2 => "foo",
%attributes,
}, $class;
}

ユーザは以下のようにしてnew()への名前付き引数としてattributeを設定できます。

my $obj = Hoge->new(attr1 => "HOGE", attr2 => "FOOBAR");

これ自体は何も難しいことはないのですが、 ドキュメントがずさんな場合 、attribute名が列挙されているだけでnew()メソッドの引数として指定できることがどこにも明記されていないことがあるので注意が必要です。


アクセサメソッドとしてのattribute

この意味でattributeを使っているクラスでは、例えば以下のようなアクセサメソッドを持ちます。

sub attr1 {

my $self = shift;
if(@_) {
$self->{attr1} = shift;
}
return $self->{attr1};
}

ユーザはこのアクセサメソッドによってオブジェクトのデータにアクセスできます。

$obj->attr1;         ## getter

$obj->attr1("foo"); ## setter

これも難しい話ではないのですが、やはり ドキュメントがずさんな場合 、アクセサメソッドの存在が明記されていないことがあります。


なぜ"attribute"が曖昧なドキュメントが生まれるのか

個人的には、Moose系モジュールの使い手がMooseの知識を前提にして書くと"attribute"の定義が曖昧なドキュメントが生まれがちだと思っています。

僕はMoose使いではないので詳しくないのですが、Moose系モジュールではオブジェクトの内部データとしてのattributeを宣言するとデフォルトで上記2 + 3(コンストラクタの引数 + アクセサメソッド)の扱いになるようです。このようなコンストラクタとattributeの仕様に慣れてしまうと、いちいちドキュメントに書くのがダルくなってしまうのでしょう。その気持ちはたしかに分かります。

とはいえ、個人やプロジェクト内で使うモジュールならともかく、CPANで一般公開するモジュールのドキュメントはMoose使いでなくても誤解なく読めるようになっているべきだと、僕は考えています。(もちろん、MooseX::系モジュールなど、そもそもMoose系モジュールの使用が必然である場合は別です)

参考:


"attribute"の曖昧性を排除したドキュメント

激しく手前味噌でアレなのですが、拙作のAsync::Queueというモジュールでは、オブジェクトの内部データとして"attribute"という言葉を使っています。さらに上記の曖昧性を排除するために、このドキュメントでは以下のような工夫をしています。


  • コンストラクタ(new()メソッド)のドキュメントに「ATTRIBUTESセクションを見よ」と明示。

  • ATTRIBUTESセクションでは、「attributeはnew()で初期化できるし、アクセサメソッドもあるよ」と言う。

  • OBJECT METHODSセクションにアクセサメソッドを列挙。

ここまで冗長な書き方はさすがにダルすぎる!ということであれば、「ここでいう"attribute"とはMooseでいうところのattributeです」と一言断っておくだけでもいいでしょう。

例えば、

=head1 ATTRIBUTES

You can get/set the following attributes like you do in L<Moose>.

=over

=item attr1 (INT, ro)

...

=item attr2 (STR, rw, default = "foo")

...

読み手にMooseの知識は要求されるものの、このように書いてあればかなりattributeの定義がクリアになると思います。