与状态monad的代理(异步)

时间:2014-01-21 19:19:45

标签: f# console monads actor

我有一系列的操作。这些操作已建模为state monads

type StateMonadBuilder<'State>() =

    // M<'T> -> M<'T>
    member b.ReturnFrom a : StateFunc<'State, 'T> = a

    // 'T -> M<'T>
    member b.Return a : StateFunc<'State, 'T> = ( fun s ->  a, s)

    // M<'T> * ('T -> M<'U>) -> M<'U>
    member b.Bind(p : StateFunc<_, 'T>, rest : 'T -> StateFunc<_,_>) : StateFunc<'State, 'U>  = 
        (fun s ->
            let a, s' = p s
            rest a s')

    member b.Zero() =
        (fun s -> (), s)


    // Getter for the whole state, this type signature is because it passes along the state & returns the state
    member b.getState : StateFunc<'State, _> = (fun s -> s, s)

    // Setter for the state
    member b.putState (s:'State) : StateFunc<'State, _> = (fun _ -> (), s) 

let runState f init = f init    

这些操作旨在并行运行。在大多数应用程序中,操作是独立执行的。 可能存在用户案例,其中操作必须从一个环境实体接收状态更新。从理论上讲,环境实体具有自己的状态,可以将其建模为状态单子本身。

我想知道如何在功能风格中解决这个问题。 我已经阅读了有关monad trasnformers的内容,但我不确定这是否可行。

(我正在尝试一个例子,但我不确定一个合适的玩具问题)

编辑3

根据以下评论和建议,我尝试建立代理商。 我的目标是将State Monad安装在代理上。这将允许我重用已经构建的代码。我也想了解并解决这个问题,以获得有关F#如何运作的见解。

我准备了以下玩具示例:

/////////////////////////////////////////////////////////////////////////////////////
// Definition of the state 
/////////////////////////////////////////////////////////////////////////////////////
type StateFunc<'State, 'T> = 'State -> 'T * 'State



/////////////////////////////////////////////////////////////////////////////////////
// Definition of the State monad 
/////////////////////////////////////////////////////////////////////////////////////
type StateMonadBuilder<'State>() =

    // M<'T> -> M<'T>
    member b.ReturnFrom a : StateFunc<'State, 'T> = a

    // 'T -> M<'T>
    member b.Return a : StateFunc<'State, 'T> = ( fun s ->  a, s)

    // M<'T> * ('T -> M<'U>) -> M<'U>
    member b.Bind(p : StateFunc<_, 'T>, rest : 'T -> StateFunc<_,_>) : StateFunc<'State, 'U>  = 
        (fun s ->
            let a, s' = p s
            rest a s')

    member b.Zero() = fun s -> (), s

    member b.Delay (f : unit -> StateFunc<_,_>) : StateFunc<'State, 'T> =
        b.Bind (b.Return (), f)

    // Getter for the whole state, this type signature is because it passes along the state & returns the state
    member b.getState : StateFunc<'State, _> = (fun s -> s, s)

    // Setter for the state
    member b.putState (s:'State) : StateFunc<'State, _> = (fun _ -> (), s) 

    // (unit -> bool) * M<'T> -> M<'T>
    member this.While (guard, body : StateFunc<_,_>) : StateFunc<'State, unit> =
        if guard () then
            this.Bind (body, (fun () -> this.While (guard, body)))
        else
            this.Zero ()





/////////////////////////////////////////////////////////////////////////////////////
// The agent
/////////////////////////////////////////////////////////////////////////////////////

let state = StateMonadBuilder<int> ()

type SonM (sonName: string) =
    let name = sonName
    member this.GetMoneyFromDad (x: int) = state {
        printfn " I am getting money from dad"
        let! currState = state.getState
        do! state.putState (currState + x)  
        do! this.ToConsole () }
    member this.GoShopping (x: int) = state {
        printfn " I am taken to the mall"
        let! currState = state.getState   
        do! state.putState (currState - x) 
        do! this.ToConsole () }
    member this.TellDad = state {
        printfn " I'll tell dad my balance "
        return! state.getState }
    member this.ToConsole () = state {
        let! mystate = state.getState
        printfn " Balance: %i" mystate }

type Agent<'T> = MailboxProcessor<'T>

type message = 
    | Shopping of int
    | Allowance of int
    | GetBalance
    | Stop

let setupAgent iv = Agent.Start (fun inbox ->   

    let aSon = new SonM ("Paul")

    let processMsg msg = state {
        match msg with
        | Shopping money ->
            printfn "Go shopping with %i " money
            do! (aSon.GoShopping money)
        | Allowance money -> 
            printfn " I got some money for you, son"
            do! (aSon.GetMoneyFromDad money) 
        | GetBalance -> 
            printfn " Calling: TellDad"
            let! balance = aSon.TellDad
            printfn " Current Balance: %i" balance 
            printfn " The balance should have been printed"
        | _ -> do printfn "Nothing to do.." }

    let rec loop () = 
        let getMsgAsync () = async {
            let! msg = inbox.Receive() 
            return  processMsg msg } 
        let p = 
            (fun s -> 
                let _, s' = (getMsgAsync () |> Async.Start) s
                getMsgAsync s')
        state.Bind ( /// ??? WIP HERE ??? )

    iv |> loop () ) 





let agent = setupAgent 100
agent.Post (GetBalance)    
agent.Post(Allowance 15)
agent.Post (GetBalance)    
agent.Post (Shopping 10)
agent.Post (Stop)

我不确定如何在定义代理的async递归循环中继续“绑定”状态。感谢。

1 个答案:

答案 0 :(得分:0)

下面这段代码可以使用。

这个想法是将每个步骤的计算视为State Monad。 但是,状态monad未在代理的异步loop中绑定。相反,状态从状态monad中解放出来并在loop中提出。

我不知道这是否是一个好的解决方案,但结果似乎是正确的。

/////////////////////////////////////////////////////////////////////////////////////
// Definition of the state 
/////////////////////////////////////////////////////////////////////////////////////
type StateFunc<'State, 'T> = 'State -> 'T * 'State



/////////////////////////////////////////////////////////////////////////////////////
// Definition of the State monad 
/////////////////////////////////////////////////////////////////////////////////////
type StateMonadBuilder<'State>() =

    // M<'T> -> M<'T>
    member b.ReturnFrom a : StateFunc<'State, 'T> = a

    // 'T -> M<'T>
    member b.Return a : StateFunc<'State, 'T> = ( fun s ->  a, s)

    // M<'T> * ('T -> M<'U>) -> M<'U>
    member b.Bind(p : StateFunc<_, 'T>, rest : 'T -> StateFunc<_,_>) : StateFunc<'State, 'U>  = 
        (fun s ->
            let a, s' = p s
            rest a s')

    member b.Zero() = fun s -> (), s

    member b.Delay (f : unit -> StateFunc<_,_>) : StateFunc<'State, 'T> =
        b.Bind (b.Return (), f)

    // Getter for the whole state, this type signature is because it passes along the state & returns the state
    member b.getState : StateFunc<'State, _> = (fun s -> s, s)

    // Setter for the state
    member b.putState (s:'State) : StateFunc<'State, _> = (fun _ -> (), s) 

    // (unit -> bool) * M<'T> -> M<'T>
    member this.While (guard, body : StateFunc<_,_>) : StateFunc<'State, unit> =
        if guard () then
            this.Bind (body, (fun () -> this.While (guard, body)))
        else
            this.Zero ()





/////////////////////////////////////////////////////////////////////////////////////
// The agent
/////////////////////////////////////////////////////////////////////////////////////

let state = StateMonadBuilder<int> ()

type SonM (sonName: string) =
    let name = sonName
    member this.GetMoneyFromDad (x: int) = state {
        printfn " I am getting money from dad"
        let! currState = state.getState
        do! state.putState (currState + x)  
        do! this.ToConsole () }
    member this.GoShopping (x: int) = state {
        printfn " I am taken to the mall"
        let! currState = state.getState   
        do! state.putState (currState - x) 
        do! this.ToConsole () }
    member this.TellDad = state {
        printfn " I'll tell dad my balance "
        return! state.getState }
    member this.ToConsole () = state {
        let! mystate = state.getState
        printfn " Balance: %i" mystate }

type Agent<'T> = MailboxProcessor<'T>

type message = 
    | Shopping of int
    | Allowance of int
    | GetBalance
    | Stop

let setupAgent iv = Agent.Start (fun inbox ->   

    let aSon = new SonM ("Paul")

    let processMsg msg = state {
        match msg with
        | Shopping money ->
            printfn "Go shopping with %i " money
            do! (aSon.GoShopping money)
        | Allowance money -> 
            printfn " I got some money for you, son"
            do! (aSon.GetMoneyFromDad money) 
        | GetBalance -> 
            printfn " Calling: TellDad"
            let! balance = aSon.TellDad
            printfn " Current Balance: %i" balance 
        | _ -> do printfn "Nothing to do.." }

    let rec loop s = async {
            let! msg = inbox.Receive() 
            let processedMsg = processMsg msg 
            let _, s' = s |> processedMsg
            return! loop s' }
    loop iv )


let agent = setupAgent 100
agent.Post (GetBalance)    
agent.Post(Allowance 15)
agent.Post (GetBalance)    
agent.Post (Shopping 10)
agent.Post (Stop)