ビルダークラスの作り方・使い方
実践F#を読んでもよくわからなかったのでサンプルをさらに簡単にした奴をメモ。
type OwnBuilder() = member x.Return(r) = printfn "Return (%A)" r r member x.Delay(f) = printfn "Delay (%A)" f fun () -> f() ;; let ownBuilder = new OwnBuilder();; let myExp = ownBuilder{ let x = 100 let y = 1 return x + y };;
として実行した結果は
type OwnBuilder = class new : unit -> OwnBuilder member Delay : f:(unit -> 'a) -> (unit -> 'a) member Return : r:'b -> 'b end val ownBuilder : OwnBuilder Delay (<fun:myExp@31-5>) val myExp : (unit -> int)
となって、Delayが自動的に実行されてその結果がmyExpへと入る。この時の引数fはownBuilder{...}の...の箇所が評価されてクロージャー(この場合 fun() -> f())として入ってくる。この結果を取得したいときは、
> myExp ();; Return (101) val it : int = 101
として、これを評価してやるとReturnメソッドが実行されてその結果の101を取得できる。
さらにこれをもうちょっと書き換えて
type OwnBuilder() = member x.Return(r) = printfn "Return (%A)" r r member x.Delay(f) = printfn "Delay (%A)" f fun () -> f() member x.Bind(p, rest) = printfn "Bind (%A, %A)" p rest rest p ;; let ownBuilder = new OwnBuilder();; let myExp = ownBuilder{ let x = 100 let! y = 1 return x + y };;
としてやる。ここでlet!はBindメソッドへと変換されるので、Bindメソッドを新たに定義している。これを実行すると
type OwnBuilder = class new : unit -> OwnBuilder member Bind : p:'a * rest:('a -> 'b) -> 'b member Delay : f:(unit -> 'c) -> (unit -> 'c) member Return : r:'d -> 'd end val ownBuilder : OwnBuilder Delay (<fun:myExp@34-6>) val myExp : (unit -> int)
Bindメソッドが追加されている以外はあまり変わったように見えない。
> myExp ();; Bind (1, <fun:myExp@36-7>) Return (101) val it : int = 101
としてここでBindメソッドが実行される。