osiire’s blog

ふしぎなそふとやさん

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;
	}	
}