2013年2月2日土曜日

Clojureで継承みたいなナニカ

Clojureのdeftypeやdefrecordではinterfaceのみを実装することができます。
しかし、継承のように実装があるclassを継承したいときがあります。

例えばこんなコードがあります。

(defprotocol Sequence
(add [xs x])
(empty [xs])
(head [xs])
(tail [xs]))
(defrecord Cons [x xs]
Sequence
(add [xs x] (Cons. x xs))
(empty [xs] (Nil.))
(head [_] x)
(tail [_] xs))
(defrecord Nil []
Sequence
(add [xs x] (Cons. x xs))
(empty [xs] (Nil.))
(head [_] nil)
(tail [xs] xs))
(defrecord Wrapped [list]
Sequence
(add [xs x]
(update-in xs [:list] (partial cons x)))
(empty [xs]
(Wrapped. '()))
(head [xs]
(first list))
(tail [xs]
(update-in xs [:list] rest)))
view raw sequence.clj hosted with ❤ by GitHub

ConsとNilのaddとemptyの実装が同じです。
共通化したい。

とりあえず、addとemptyを別Protocolに。

(defprotocol Sequence
(head [xs])
(tail [xs]))
(defprotocol Adder
(add [xs x])
(empty [xs]))
view raw adder.clj hosted with ❤ by GitHub

ListというProtocolを作って、それに対するAdderを定義します。

(defprotocol List
(list [xs]))
(extend-type List
Adder
(add [xs x] (Cons. x xs))
(empty [xs] (Nil.)))
view raw list.clj hosted with ❤ by GitHub

ConsとNilに対してListを実装すれば、めでたしめでたし。

(defrecord Cons [x xs]
List
(list [xs] xs)
Sequence
(head [_] x)
(tail [_] xs))
(defrecord Nil []
List
(list [xs] xs)
Sequence
(head [_] nil)
(tail [xs] xs))
view raw cons.clj hosted with ❤ by GitHub

0 件のコメント:

コメントを投稿