Stateモナド

Stateモナドは、状態付き計算を行うことができます。 Stateモナドを実装するためのState型を次に示します。
Stateモナド
type State<'s, 'a> = State of ('s ->'a * 's)
('s ->'a * 's)の個所が(状態 -> 結果 * 次の状態)を表しています。 別に、結果と次の状態はこの順番でなくともよいのですが、慣例に従って'a * 'sとしています。 これを用いたサンプルプログラムが次のコードです。
Stateモナド
type State<'s, 'a> = State of ('s ->'a * 's)

(*
//Runを使わないバージョン
let (>>=) (State g) f =
 State <| fun s ->
  let v,s' = g s
  match f v with
        | State h -> h s'
*)
let Run state (State s) = s state
let (>>=) m f =
  State <| fun s ->
   let v,s' = Run s m
   Run s' <| f v
type StateBuilder () = 
  member this.Bind(m, f) = m >>= f
  member this.Return x = State <| fun s -> x, s
let state = StateBuilder () 
//stateモナドを操作する関数群
let Get = State <| fun s -> (s, s)
let Getf = fun _ -> State <| fun s -> (s,s)
let Put s = State <| fun _ -> ((), s)
let Modify f = state {
                let! s = Get
                do! Put (f s)
                }
let Test1 = state{
    let! a = Get             // a=10 (10,10)
    do! Put (a+5)            // ((),15)
    do! Modify ((+)5)        // ((),20)
    let! b = Get             // b=20 (20,20)
    let! d = Modify ((+)10)  // d=() ((),30)
    return b                 // (20,30)
    }
Test1 |> Run 10 |> printfn "%A" 


let Test2 = state{
    let! a = Getf ()      //a=[1;2] ([1;2],[1;2])
    let! b = Put (3::a)   //b=() ([(),[3;1;2])
    let! c = Getf ()      //c=[3;1;2] ([3;1;2],[3;1;2])
    return (List.sum c)   //(6,[3;1;2])
    }
Test2 |> Run [1;2] |> printfn "%A" 

StateモナドのBindとReturnはMaybeモナドに比べると少しトリッキーです。 Bindはまず与えられた状態sを使って次の状態s'を計算し、その結果にfを適用していきます。 Returnは与えられたxをState型の結果の個所に用います。 Runは状態とState型を受け取って(結果、状態)を返す関数です。 基本的にはStateモナドを使って計算を行った後に結果を求めるのに使いますが、 ここではBindの実装にも使っています。 state{}ブロックをみていくと、初期値がなくいきなりGetしています。 これは、Stateが状態を持っているわけではなく状態付きの計算を表しているからです。 初期値は後でRunするときに与えることになります。 let! a = Getとすると(結果,状態)のうち結果の値がaに入ります。 これは、結果の値が()になるPutやModifyをlet!で値を受け取ってみると ()になることからもわかります。 Test1では、dの値が()になっています。 Test2では、GetではなくGetfを使っています。 Test1でGetを使ったときにState型の型変数の値が数値型に固定されるため Test2でGetを使うと型エラーが出てしまうためです。 Test1を消せばTest2でもGetで動作しますが そういう時にGetf ()を使うと別のState型として実体化されるのでエラーが出なくなります。 Stateモナドを使いこなすには、GetとPutについてもよく理解しておく必要があります。 Test1,Test2のコメントに具体的な値の変化を記しているので参考にしてみてください。