新しいblogに移行しました

新ブログ "All Yout Bugs Are Belong To Ass" に移行しました!

2011-02-01

[Perl]インスタンスベースOOPを少し楽に書けるPlassってモジュールを書いた

インスタンスベースOOPって聞いたことありますか?ぶっちゃけ、僕もついこの前までこの用語を知らなかったです!(ちなみに「プロトタイプベース」っていう用語としてなら、それなりに普及しているっぽいんですけど。。。)

要するに、JavaScriptにおけるOOPみたいなものだと思ってもらえれば良いみたいです。
で、Perlでもそういうことを簡単にやってのけるような実装が無いわけじゃないみたいですが(インスタンスベースOOPで必要な機能を部分的にカバーするモジュールなら幾つかある)、あくまで「インスタンスベースOOPの面倒をみてやるぜー」的なモジュールとなると、そういうのは見つけられなかったです。
また、Perlの言語レベルである程度カバーできる箇所もあるんですけど、それってキモチワルい見た目のソースを書くことにつながったりするので、そういうのはナシって方向で考えると、

「あれ、PerlでインスタンスベースOOP実現するのって、なんかすっきりしたやり方が無い様な。。。」

という結果に行き着いてしまいました。。。
そんなわけで、インスタンスベースOOPの面倒を見るモジュールとしてPlassというのを作ってみました!

こんな感じで使えます

ちなみに以下のソースは、Cyanというインスタンスベースな言語のドキュメントの一部にあったサンプルソースです。
Human = Object.child()
Human.walk = ^(self, dir){
  say(self.name + " walked " + dir + ".")
}

Superman = Human.child()
Superman.fly = ^(self, dir){
  say(self.name + " flew " + dir + ".")
}

human = Human.child()
human.name = "John"
human.walk("east")  #=> John walked east.

superman = Superman.child()
superman.name = "Clark"
superman.fly("north")  #=> Clark flew north.
superman.walk("west")  #=> Clark walked west.

これをPerlでPlassを使って書き換えると、以下の様になります。
use warnings;
use strict;
use Plass;

# plassコマンドでPlassオブジェクトのクローンを作成
my $Human = plass; 

# Humanインスタンスにwalkメソッドを追加
$Human->method( walk => sub {
    my ( $self, $dir ) = @_;
    print $self->name. " walked ". $dir. ".\n";
} );

# SupermanインスタンスはHumanインスタンスのクローン
my $Superman = $Human->plass;

# Supermanインスタンスにflyメソッドを追加
$Superman->method( fly => sub {
    my ( $self, $dir ) = @_;
    print $self->name. " flew ". $dir. ".\n";
} );

# humanインスタンスはHumanインスタンスのクローン
my $human = $Human->plass;

# humanインスタンスのname値を設定
$human->name( "John" );

# Humanインスタンスから継承したwalkメソッドを実行
$human->walk( "east" );

# supermanインスタンスはSupermanインスタンスのクローン
my $superman = $Superman->plass;

# supermanインスタンスのname値を設定
$superman->name( "Clark" );

# Supermanインスタンスから継承したflyメソッドとwalkメソッドを実行
$superman->fly( "north" );
$superman->walk( "west" );

出力結果は
John walked east.
Clark flew north.
Clark walked west.
となり、Cyanのそれと同じ出力(Cyanのドキュメントによると、ですが^^;)になっていることがわかります。

Plassではインスタンス継承時にメンバ変数やメソッドを一気に被せることもできますので、上のコードは以下のように書くことが可能です。
use warnings;
use strict;
use Plass;

# Humanインスタンスの定義
my $Human = plass(
  walk => sub {
    my ( $self, $dir ) = @_;
    print $self->name. " walked ". $dir. ".\n";
  }
);

# Supermanインスタンスの定義
my $Superman = $Human->plass(
  fly => sub {
    my ( $self, $dir ) = @_;
    print $self->name. " flew ". $dir. ".\n";
  }
);

# humanインスタンスの定義
my $human = $Human->plass( name => "John" );

# humanインスタンスのwalkメソッドを実行
$human->walk( "east" );

# supermanインスタンスの定義
my $superman = $Superman->plass( name => "Clark" );

# supermanインスタンスのflyメソッドおよびwalkメソッドを実行
$superman->fly( "north" );
$superman->walk( "west" );

出力結果は
John walked east.
Clark flew north.
Clark walked west.
先ほどと同様ですね。

もし興味を持っていただけたら


ytnobody/Plass - GitHub

にアップしてありますので、お気軽にダウンロードしていただければ幸いです!
但し現在のところ、圧倒的にテストが不足してますので、そこんとこは予めご了承ください。

ちなみに僕はインスタンスベースOOP言語は今までJavaScriptしか触ったことが無いです(と思います)。

もし「真のインスタンスベースとはここが違うじゃねえか!」などというツッコミとか「こうした方が使いやすいんじゃないか」などのご提案、「これをこう使うと変になる!」みたいなバグ報告などなどございましたら、@ytnobodyまでご連絡ください!

余談ですが


Cyan言語の開発者はなんと、高校生の頃にCyanを開発したそうですよ!猛者ですね!Cyan言語についても興味がでてきました!

0 件のコメント: