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




Some 20

[10; 20; 30; 40; 50]
続行するには何かキーを押してください . . .

lightweight syntaxをonにするかoffにするかで心に迷いが生じています。今日は何となくoffで書いてみました。
ちなみにlet! などは let bang と発音するらしいですね。