Eliminating my explicit state passing via like, monads and stuff

前端 未结 2 632
傲寒
傲寒 2020-12-09 20:11

I\'m working through the book Land of Lisp in F# (yeah weird, I know). For their first example text adventure, they make use of global variable mutation and I\'d li

相关标签:
2条回答
  • 2020-12-09 20:43

    If you want to use mutable state in F#, then the best way is just to write a mutable object. You can declare a mutable Player type like this:

    type Player(initial:Location, objects:ResizeArray<Thing>) =
      let mutable location = initial
      member x.AddThing(obj) =
        objects.Add(obj)
      member x.Location 
        with get() = location
        and set(v) = location <- v
    

    Using monads to hide mutable state isn't as common in F#. Using monads gives you essentially the same imperative programming model. It hides the passing of state, but it doesn't change the programming model - there is some mutable state that makes it impossible to parallelize the program.

    If the example uses mutation, then it is probably because it was designed in an imperative way. You can, change the program architecture to make it more functional. For example, instead of picking the item (and modifying the player), the pickUp function could just return some object representing a request to pick the item. The world would then have some engine that evaluates these requests (collected from all players) and calculates the new state of the world.

    0 讨论(0)
  • 2020-12-09 20:48

    If you want to use the state monad to thread the player's inventory and world state through the pickUp function, here's one approach:

    type State<'s,'a> = State of ('s -> 'a * 's)
    
    type StateBuilder<'s>() =
      member x.Return v : State<'s,_> = State(fun s -> v,s)
      member x.Bind(State v, f) : State<'s,_> =
        State(fun s ->
          let (a,s) = v s
          let (State v') = f a
          v' s)
    
    let withState<'s> = StateBuilder<'s>()
    
    let getState = State(fun s -> s,s)
    let putState v = State(fun _ -> (),v)
    
    let runState (State f) init = f init
    
    type Location = Room | Garden
    type Thing = { Name : string; Article : string }
    type Player = { Location : Location; Objects : Thing list }
    
    let pickUp thing =
      withState {
        let! (player, objects:Map<_,_>) = getState
        let objs = objects.[player.Location]
        let attempt = objs |> List.partition (fun o -> o.Name = thing)    
        match attempt with    
        | [], _ -> 
            return "You cannot get that."
        | thing :: _, things ->    
            let player' = { player with Objects = thing :: player.Objects }        
            let objects' = objects.Add(player.Location, things)
            let msg = sprintf "You are now carrying %s %s" thing.Article thing.Name
            do! putState (player', objects')
            return msg
      }
    
    let player = { Location = Room; Objects = [] }   
    let objects =
      [Room, [{ Name = "whiskey"; Article = "some" }; { Name = "bucket"; Article = "a" }]
       Garden, [{ Name = "chain"; Article = "a length of" }]]    
      |> Map.ofList
    
    let (msg, (player', objects')) = 
      (player, objects)
      |> runState (pickUp "bucket")
    
    0 讨论(0)
提交回复
热议问题