Functional Reactive F# - Storing States in Games

后端 未结 5 1529
借酒劲吻你
借酒劲吻你 2021-01-31 04:34

I am a student currently learning about Functional Reactive paradigm using F#. It\'s radically new viewpoint for me. Yesterday I learned about creating a simple ping-pong game u

5条回答
  •  甜味超标
    2021-01-31 05:22

    I haven't got any experience with reactive programming under F#, but the problem of global state in purely functional systems is quite common and has a quite elegant solution: Monads.

    While monads themselves are primarily used in Haskell, the underlying concept made it into F# as computation expressions.

    The idea is that you don't actually change states but just describe the transitions of the state, i.e. how to produce new states. The state itself can be completely hidden in the programm. By using special monadic syntax, you can write pure but stateful programms almost imperatively.

    Taking a (modified) implementation from this source, the State monad could look like this

    let (>>=) x f =
       (fun s0 ->
          let a,s = x s0    
          f a s)       
    let returnS a = (fun s -> a, s)
    
    type StateBuilder() =
      member m.Delay(f) = f()
      member m.Bind(x, f) = x >>= f
      member m.Return a = returnS a
      member m.ReturnFrom(f) = f
    
    let state = new StateBuilder()     
    
    let getState = (fun s -> s, s)
    let setState s = (fun _ -> (),s) 
    
    let runState m s = m s |> fst
    

    So let's have an example: We want to write a function that can write values into a log (just a list) while proceeding. We therefore define

    let writeLog x = state {
      let! oldLog = getState // Notice the ! for monadic computations (i.e. where the state is involved)
      do! setState (oldLog @ [x]) // Set new state
      return () // Just return (), we only update the state
    }
    

    Within state, we can now use this in an imperative syntax without having to handle the log list manually.

    let test = state {
       let k = 42
       do! writeLog k // It's just that - no log list we had to handle explicitly
       let b = 2 * k
       do! writeLog b
       return "Blub"
    }
    
    let (result, finalState) = test [] // Run the stateful computation (starting with an empty list)
    printfn "Result: %A\nState: %A" result finalState
    

    Still, everything is purely functional here ;)

提交回复
热议问题