Observerパターンを内包して合成できるようにした何か
先日いげ太さんに紹介されたF#のイベント(http://research.microsoft.com/en-us/um/cambridge/projects/fsharp/manual/FSharp.Core/Microsoft.FSharp.Control.Event.html)が面白かったので、Objective Camlでも(一部を)実装してみました。↓
http://forge.ocamlcore.org/plugins/scmsvn/viewcvs.php/trunk/observer.ml?rev=31&root=ccell&view=markup
これを使うと、例えば、キーボードのqが押されたらアプリケーションを終了するイベントをこう書けます。
let (>>!) = Observer.listen let (>>|) = Observer.filter window_event >>| key_filter K._q >>! (fun _ -> exit 0)
他にも、マウスのX座標を表現するDVarとか。
let (+>) f g = f g let mouse_x = let mouse_x_filter = function `PT_MOVE _ as ev -> Some (E.pt_location ev)#x | _ -> None in (window_event >>| mouse_x_filter) +> Observer.to_dvar 0.
mouse_xには常にマウスカーソル位置のX座標が入っている状態になりますです。
あと、よく考えてみるとこれってOCamlじゃなくても余裕で出来そう。という訳でhaXeでもやってみたのでコードだけ晒しておきます。
Observerクラスの__fireメソッドを例えばon_loadイベントのコールバック関数として登録しておけば、on_loadオブザーバーの出来上がりです。
package itpl.util; import itpl.util.Util; class Observer<A> { var listener : Array<A -> Void> ; public function new() { listener = new Array(); } public function listen( l : A -> Void) : Void { listener.push( l ); } public function clear_listener() : Void { listener = new Array(); } public function __fire( x : A ) : Void { for( l in listener ) { l ( x ); } } public function map<B> ( f : A -> B ) : Observer<B> { var o : Observer<B> = new Observer(); this.listen( function(x) { o.__fire(f(x)); } ); return o; } public function filter<B> ( f : A -> Option<B> ) : Observer<B> { var o : Observer<B> = new Observer(); this.listen( function(x) { return switch (f(x)) { case Some (v) : o.__fire( v ); case None : null; } }); return o; } public function merge<B> ( o1 : Observer<B> ) : Observer<Ether<A,B>> { var o : Observer<Ether<A,B>> = new Observer(); this.listen ( function(x) { o.__fire ( Left(x) ); } ); o1.listen ( function(x) { o.__fire ( Right(x) ); } ); return o; } }