Fool Pool

ハマった記

Perl で Javascript のようにプログラミングする — プロトタイプチェーンの実装

JavascriptでできることはたいていPerlでもできる。

Perl にはコードレフやハッシュリテラル記法があるので、見た目はともかく(javascriptならドット一つで済むところを、Perlだと毎回アローとブレースを書く必要がある)、機能的には Perl でも javascript と遜色ない使い方ができると思っている。

"Javascript The Good Parts" を読んでいたら、おなじ仕組みを Perl で実装してみたくなった。

要件

  • クラスからの継承ではなく、プロトタイプ継承
  • プロパティやメソッドがオブジェクトに定義されていない場合、プロトタイプチェーンを遡って検索する

利用イメージ

my $vehicle = create();
my $car     = inherit($vehicle);
my $ferrari = inherit($car);

# メソッド定義は関数リテラル(コードレフ)で
$vehicle->{drive} = sub {say "(make some noise...)"};
$car->{drive}     = sub {say "Broom!"};

# apply を呼ぶと、プロトタイプチェーンを遡ってメソッドを検索してくれる
apply($vehicle,"drive"); # STDOUT> (make some noise...)
apply($car    ,"drive"); # STDOUT> Broom!
apply($ferrari,"drive"); # STDOUT> Broom!

仕様

  • オブジェクトの実体はハッシュ
  • 継承後のオブジェクトは、ひな形となるオブジェクトのリファレンスを格納した prototype というプロパティをもつ
  • オブジェクトにプロパティやメソッドが定義されていない場合、prototype プロパティから親オブジェクトのプロパティを再帰的にたどる

実装

  • create : ルートオブジェクト(すべてのオブジェクトの共通の祖先)をひな形にして新たなオブジェクトを生成する
  • inherit : 既存のオブジェクトをひな形にして新たなオブジェクトを生成する
  • apply : プロトタイプチェーンを遡ってメソッドを検索する
use Carp qw(croak);

# ルートオブジェクト(すべてのオブジェクトの共通の祖先)をひな形にして新たなオブジェクトを生成する
sub create {
    my $_Object = {
        prototype => undef,
    };
};

# 既存のオブジェクトをひな形にして新たなオブジェクトを生成する
sub inherit {
    my $this = shift;
    {
        prototype => $this,
    };
}

# プロトタイプチェーンを遡ってメソッドを検索する
sub apply {
    my ($this, $msg) = (shift, shift);
    if (!defined $this) {
        croak "can't apply $msg to an undefined object.";
    }

    if ( defined $this->{$msg} ) {
        $this->{$msg}($this, @_); 
    }
    elsif ( defined $this->{prototype} ) {
        my $proto = $this->{prototype};
        apply($proto, $msg, @_);
    }
    else {
        croak "no message '$msg' found on the prototype chain.";
    }
}

一応、最低限の例外処理も実装してある:
1. レシーバが定義されていない場合
2. プロトタイプチェーン上のどこにもメソッドが定義されていない場合

apply($ferarri,"dive"); # Oops! ferarri can't dive!
apply($honda,"drive");  # Oops! honda isn't defined!

[追記] 今回実装した apply メソッドでは、プロパティを再帰的にたどることはできない。プロパティもメソッドも両方チェーンを辿れるようにするには、applyメソッドで、ハッシュの値がコードレフかそれ以外かを判定すれば良い。

参考文献

[1] Amazon.co.jp: JavaScript: The Good Parts ―「良いパーツ」によるベストプラクティス: Douglas Crockford, 水野 貴明: 本
[2] Amazon.co.jp: 7つの言語 7つの世界: Bruce A. Tate, まつもとゆきひろ, 田和 勝: 本

ちなみに、vehicleとか ferrari の例は、[2]に出てくる、IOという言語のサンプルコードに出てきた変数名を使わせてもらった。IOもJavascriptと同様に、プロトタイプ継承をもつ言語である。

英語版だと電子書籍がフリーで配布されている。