LoginSignup
14
13

More than 5 years have passed since last update.

JS-CLOSで多重ディスパッチと多重継承

Last updated at Posted at 2013-02-21

Common Lisp Object System(CLOS)に似たオブジェクトシステムをJavaScriptで使えるJS-CLOSの紹介です。
多重継承、多重ディスパッチ(総称関数)、インスタンス作成時の簡単な型チェックなどができます。

CLOSではオブジェクトは単なるスロットの集合で、メソッドはオブジェクトに属しません。メソッドに相当するのは総称関数です。
CLOSについての詳しい説明はwikipedia等を参照してください。

まずは小手調べ。MLやHaskellのパターンマッチみたいなことをしてみます。

fib.coffee
fib = define_generic()

define_method fib, [0], -> 0
define_method fib, [1], -> 1
define_method fib, ["number"], (n) ->
  fib(n - 1) + fib(n - 2)

fib 20
#=> 6765
  • define_generic() は総称関数を作ります。
  • define_method 総称関数, [パターン], メソッド は 総称関数 が適用される際に パターン に適合する実引数が与えられたら メソッド を呼ぶよう設定します。複数のパターンにマッチする時は、一番特殊なパターンが選択されます。

パターンは、

  • 具体値として === で比較されるオブジェクト
  • undefined --ワイルドカード
  • typeof で返される文字列 ("string", "function", など)
  • instanceofで比較されるクラス (コンストラクタ関数) のどれかです。

次に多重ディスパッチの例です。

bump.coffee
floor  = define_class []
carpet = define_class []
ball   = define_class []
glass  = define_class []

bump = define_generic()

define_method bump, [ball, floor],        -> 'bounce'
define_method bump, [glass, floor],       -> 'crash'
define_method bump, [undefined, carpet],  -> 'silence'

bump new ball, new floor                 #bounce
bump (make ball), (make floor)   #bounce

bump new glass, new floor                #crash

bump new ball, new carpet                #silence
bump new glass, new carpet               #silence
  • define_class [親], バリデータ はクラスを作ります。これはnew演算子、またはmake関数を適用するとインスタンスを生成する関数です。

最後に多重継承の例。

lion.coffee
# domain
animal = define_class [], (x) ->
  slot_exists x, 'voice', 'string'

cat = define_class [animal]     #猫は動物のスロットを継承

osx = define_class [], (x) -> 
  slot_exists x, 'hostname', 'string'

lion = define_class [cat, osx]       #ライオンは猫とOSX両方のスロットを継承

# definitions

say = define_generic()
define_method say, [animal], (a) -> a.voice 
define_method say, [cat], (c) -> c.voice + ' meow'

hostname = define_generic()
define_method hostname, [osx], (o) -> o.hostname

# main

alice = make lion,
             voice: 'roar'
             hostname: 'example.com'

say alice
#=> 'roar meow'

hostname alice
#=> 'example.com'
  • slot_exists オブジェクト, キー, パターン は、*オブジェクト に キー が存在し、かつ パターン にマッチする値が格納されている場合にtrueを返します。
  • define_classに第2引数として与えられた関数は、インスタンス生成時のスロット初期化の際に呼ばれます。falseを返すと初期化を失敗させることができます。

菱形継承問題などのコーナーケースはだいたい考慮されていません。

よかったら遊んでみてください。プルリクエストお待ちしています。

14
13
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
14
13