Workflowでモナド
seqやasyncの正体はWorkflowというもののようだ。
正確にはComputation Expressionsというのかな?(なんて訳すとカッコいいんでしょう。)
CPS変換のシンタックスシュガーみたいだけど、なんとなくモナドに似た感じもある。
最近F#の勉強がおろそかになっていたことですし、ここはHaskellのMaybeモナド、Listモナドを実装してみることでComputation Expressionsの練習をしよう、ということで以下のようになりました。なんとか動いているようです。ピクピクしています。
let! が Bind に、
return が Return に対応します。
#light "off" (* (Just x) >>= f = f x Nothing >>= f = Nothing return x = Just x *) module MaybeMonad = begin type MaybeBuilder() = class member self.Bind (exp, f) = match exp with | Some x -> f x | None -> None member self.Return x = Some x member self.Delay f = f () end let maybe = MaybeBuilder() end module Test_Maybe = begin open MaybeMonad let private f p = function Some x when p x -> Some x | _ -> None let private f1 = f <| (>=) 100 let private f2 = f <| (<=) 0 let private f3 = f (fun n -> n%2=0) (* 仕様書によると、 let! pat = expr in cexpr は、 b.Bind(expr, (fun pat -> cexpr)) になるとのこと *) let private Test' n = maybe { let! a = f1 <| Some n in let! b = f2 <| Some a in let! c = f3 <| Some b in return c } (* 糖衣を剥ぐと、実際はこんな感じになるらしい *) let private Test'' n = maybe.Delay (fun () -> maybe.Bind (f1 <| Some n, (fun a -> maybe.Bind (f2 <| Some a, (fun b -> maybe.Bind (f3 <| Some b, (fun c -> maybe.Return c ))))))) let Test () = let p = printfn "%A" in p <| Test' -1 ; p <| Test'' 120 ; p <| Test' 55 ; p <| Test'' 20 ; //p <| (Some 10 |> f1 |> f2 |> f3) // こっちの方が! end (* xs >>= f = concatMap f xs return x = [x] *) module ListMonad = begin type ListBuilder() = class member self.Bind (exp, f) = List.collect f exp member self.Return x = [x] member self.Delay f = f () end let list = ListBuilder() end module Test_List = begin open ListMonad let private Test' () = list { (* いい例が思い浮かばなかった。。 *) let! a = [ for x in 1..5 -> [[x]] ] in let! b = List.map id a in let! c = List.map (( * )10) b in return c } let Test () = printfn "%A" <| Test' () end #if COMPILED [<System.STAThread()>] do Test_Maybe.Test () ; printfn "" ; Test_List.Test () #endif
lightweight syntaxをonにするかoffにするかで心に迷いが生じています。今日は何となくoffで書いてみました。
Some 20[10; 20; 30; 40; 50]
続行するには何かキーを押してください . . .
ちなみにlet! などは let bang と発音するらしいですね。