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を返すと初期化を失敗させることができます。
菱形継承問題などのコーナーケースはだいたい考慮されていません。
よかったら遊んでみてください。プルリクエストお待ちしています。