「世間にいろいろ是非論はあるが、ぼくはなぜPerlが好きなのか、このさいいろいろ考えてみる」の続き。
昨日のまとめは以下の通り。

 ・Perlでは変数に$、@、&というシジル(ファニー文字)を付ける
 ・これを活用すると、英単語をそのままプログラムに使えるので、読みやすいプログラムが出来る
 ・LarryによるとシジルはPerlが成長するために必要だということだ(将来に渡って予約語と衝突しないため)

さて今日は、Perlによるオブジェクト指向について書く。
Perlのオブジェクト指向は評判が悪い。

まず、後付けである。
これは、昔のPerlはOOPではなくて、その後Perl5になってOOPになったのだから、まごうかたなき事実である。

だから、PerlのOOPは不恰好で分かり難い、と言われる。
そうだろうか。
実際、ぼくはPerlでOOPを書いて初めて、OOPの何たるかが分かった。
「OOPでない言語にOOPを実装した過程を通じて、ユーザーにもOOPの本質が分かるようになった」のである。

OOPの解釈はいくつかあるが、ここでは「変数の型をプログラマーが定義してプログラム内で使うこと」と考える。
たとえば「日付型」の変数を作って年月日を代入する。
曜日を聞くと曜日が帰ってくる。
日付型の変数に1000を足すと、月がわり、年がわりを考慮して千日後の日付が帰る。
みたいなことである。
便利だ。

たとえば「人間型」の変数を定義して人間を作る、というようなことが出来る。

下のプログラムはPerlで作ったOOPの最小限のサンプルであるが、myPerson型で「人間型」の変数を作っている。
人間には生年月日、身長、体重、性別、血液型などさまざまな要素があるが、ここでは「人間とは名前を持つものである」と定義し、名前だけをその型の属性(プロパティ)として持つ型(クラス)を考える。

 #! /bin/perl
 #
 # objExp.pl -- オブジェクトの実験
 #
 # sample file name: objExp.pl

 #### クラス定義

 package myPerson;

 use overload
  qw("") => "toString",
 ;

 sub new {
  my ($class, $name) = @_;
  bless { name => $name }, $class;
 }

 sub toString {
  my $self = shift;
  return $self->{name};
 }

 sub DESTROY {
  my $self = shift;
  print "Oh, $self is dying ! \n";
 }
 
 #### 以下メインルーチン

 package main;

 my $man = myPerson->new("Jack");
 print "He may be $man \n";

オブジェクトを作るために必要最小限なメソッドはコンストラクターnewである。
これはnewという名前でなくてもいいが、特に必要がなければ、こんなところで個性を発揮してもしょうがないので先輩に習ってnewと付ける。
(PerlでデータベースをアクセスするDBIモジュールではnewの代わりにconnectというメソッド名を付けている。新しいDBとのアクセスを確立するためにconnectと言うわけで、分かりやすい)

myPerson->new("Jack")という呼び方をすることによって、myPersonクラスのコンストラクターnewが呼び出されるが、この時第1の引数にクラス名(パッケージ名)myPersonが付加される。

  my ($class, $name) = @_;

ここで$classにはクラス名'myPerson'が、$nameには呼び出したときの引数'Jack'が入っている。

  bless { name => $name }, $class;

{ name => $name }はnameというキー値に$name(ここでは'Jack')という値を持つ値を関連付けた無名ハッシュリファレンスである。
Perlではスカラーも、配列もオブジェクトに出来るが、普通は柔軟性を考えてハッシュにする。
bless関数によってこのハッシュリファレンスは自分が$classパッケージに属するという自覚が生じる。
blessは英語で祝福するという意味だが、洗礼によってキリスト者にすることだ。
まあ、仏教式に得度という理解でもいいだろう。
ぼくはヤクザが「兄弟分の盃をもらう」という理解がいいと思っている。

newメソッドはmyPersonクラスに型付けられたハッシュリファレンスを返す。

 my $man = myPerson->new("Jack");

によって$manという変数は、myPersonクラスであるという自覚を持つハッシュリファレンスになる。

 print "He may be $man \n";

という文では、$manという変数を含む文字列を表示している。
Perlは二重引用符の中に変数を含んでいるとそれに勝手に展開してくれる(たとえば文字列を格納していればその文字列値で置き換わる)が、$manがどのように展開されるかはクラスの中で演算子のオーバーロードを行うことで定義できる。

 use overload
  qw("") => "toString",
 ;

これで、二重引用符の中でどう展開されるかを決めている。
ここではtoStringというメソッドを呼び出している。

 sub toString {
  my $self = shift;
  return $self->{name};
 }

第1引数$selfはオブジェクトへのリファレンスを得る。
戻り値は$self->{name}、つまり、$selfをデリファレンス(参照解決)したハッシュのnameというキーの値(ここでは'Jack')になる。

よって

 print "He may be $man \n";

という文を実行すると

 He may be Jack

という文字列が表示される。

以上、駆け足で紹介したが、これだけ読んでも分からないかもしれない。
より詳しくは左のサイドバーで紹介している拙著『すぐわかるオブジェクト指向Perl』をご覧いただきたい。

上記の例で雰囲気はお伝えできたと思うが、Perlではクラスやオブジェクトをかなりユーザーに手作りさせる部分が大きい。
Perlが組み込みで与えているのはpackage文、bless関数、そして矢印記法->によるメソッド呼び出しだけである。
変数を何型で実装するか(上はハッシュを使っている)、コンストラクターを何という名前にするか(上ではnewにしている)などはユーザーの裁量に任されている。
それだけに、一度理解できれば(少なくとも自作の)クラスの深層/真相が理解できる。

それに対して、Pure OOP言語はオブジェクト機能があらかじめ備わっている。
最初から「こうするとクラスが出来ますよ」「こうするとオブジェクトが出来ますよ」という構文を覚えて作る。

どっちが分かりやすいだろうか。

OOP言語の目的の一つは隠蔽(難しいことを隠す、意識させないようにする)であって、そういう意味ではPure OOP言語の方が明らかに分かある。

しかしPerlのOOPは「OOPって要するにこういうことでしょ」「非OOP言語にこれだけ追加すればOOPになるんじゃないの」ということを舞台裏から解説してくれているような味がある。

自動車教習所に行ったらまずエンジンの組み立てから習うようなものである。
そんなことやりたいですか。
ぼくはやりたいと思う。
エンジンの組み立てを学ぶのは楽しいし、理解することが運転技術の向上につながる「こともある」と思うのだ。

ここ数日のブログはPerlを必死に擁護しているような流れであって、ムリヤリ褒めているように思われるかもしれない。
しかしぼくは、Perlでクラス、オブジェクト、メソッドを作って、演算子のオーバーロードを行って、はじめてOOPの思想を理解した。
これは本当の話だ。
OOPでない言語が、OOPになる過程を通じて、OOPのありがたみが分かったのである。

ということで、PerlにOOPが実装される過程に対する、ぼくの理解を書いたのが拙著『すぐわかるオブジェクト指向Perl』である。
Perlがなじめない人も、OOPがなじめない人はご一読をお勧めする。
無駄に分厚いけど文章がダラダラしているのでこたつみかんですぐ読めると思う。



付記:OOPでない言語がOOPになったというとCがC++になったのもそうである。
最初はC++のソースはトランスレーターでCのソースに変換して、そのCのソースをコンパイルして実行形式を作っていた。
で、この中間のCのソースを読み込むことによってOOPとは何かを考えようという本もあった。
工藤智行さんの書いた下の本がそうだ。



で、この工藤さんがぼくの会社の先輩で、ぼくを技術評論社に紹介してくれた人でもある。
これも機会があったらみなさん読んでください。
あと、工藤さんは逆のアプローチでLispにOOPを実装することでOOPを理解するという本も書いていた。



OOのLispだからOOLという処理系である。
こっちは読了していないが(スミマセン)いかにも面白そうな本である。

Subscribe with livedoor Reader
Add to Google
RSS
このエントリーをはてなブックマークに追加