문제

I have a series of operations. The operations have been modeled as 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    

The operations have been designed to run in parallel. In most of the applications the operations are executed independently. There might be a user case where the operations must receive status updates from one environmental entity. In theory, the environmental entity carries its own state and it could be modeled as a state monad itself.

I was wondering how this could be solved in functional style. I have read something about monad trasnformers, but I am not sure this it is the way to go.

(I am trying to work on an example, but I am not sure about a proper toy problem)

EDIT 3

Based on the comments and the suggestions below, I have tried to build an agent. My goal is to mount the State Monad on the agent. That will allow me to reuse the code already built. I'd also would like to understand and solve this problem, in order to gain insights about how F# works.

I have prepared the following toy example:

/////////////////////////////////////////////////////////////////////////////////////
// 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)

I am unsure on how to proceed to 'bind' states within the async recursive loop that defines the agent. Thanks.

도움이 되었습니까?

해결책

This piece of code below works.

The idea is to consider each step's calculations as a State Monad. However, the state monad is not binded in the agent's async loop. Instead, the state is unwrapped out of the state monad and put forward in loop.

I don't know if this is a good solution but the results appear to be correct.

/////////////////////////////////////////////////////////////////////////////////////
// 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)
라이센스 : CC-BY-SA ~와 함께 속성
제휴하지 않습니다 StackOverflow
scroll top