Functional Banana Traveller - putting together Beh

2019-07-13 18:53发布

问题:

The problem is that I do not know how to create the Behavior of type Behavior t GameState

I have more code, but am trying to just show what I think is neccessary to talk about the problem. Let me know if there are blanks to be filled in. Here's what I have :

data GameState = GameState {agent    :: Agent
                           ,universe :: Universe
                           }

type Universe = Gr Planet ()

data Command = Move PlanetName
             | Look
             | Quit
                 deriving Show

data PlayerCommand = PlayerCommand Command PID
                   | Null
                       deriving Show

updateGS :: PlayerCommand -> GameState -> GameState
updateGS (PlayerCommand (Move planet) pid) gs =
   let agent = getAgent pid gs
       nodes = labNodes $ universe gs
       current = location agent
       Just fromP = lookup (fromEnum current) nodes
       Just toP   = lookup (fromEnum planet) nodes
       fromNode = fromEnum current
       toNode = fromEnum planet
       uPlayer = Player pid (getPlanetName toP) (Location planet)
       mData = MoveData uPlayer (toNode,toP) (fromNode,fromP) nodes
       uPlanets = updateLNodeList mData
   in GameState uPlayer (mkGraph uPlanets $ labUEdges gates

initialGS :: GameState
initialGS = GameState initPlayer (makeUniverse makePlanetNodes)

and the event network

makeNetworkDescription :: AddHandler PlayerCommand -> IO EventNetwork
makeNetworkDescription addCommandEvent = compile $ do
   eInput <- fromAddHandler addCommandEvent
   let bCommand = stepper Null eInput
   eCommandChanged <- changes bCommand
   let bGameState :: Behavior t GameState
       bGameState = stepper initialGS 
   reactimate $ (\n -> appendFile "output.txt" ("Command is " ++ show n)) <$> eCommandChanged

I believe bGameState needs to use eCommandChange, but I run into a problem with the types

stepper :: a -> Event t a -> Behavior t a

this leads me to believe I need to transform eInput :: Event t PlayerCommand into a eGameState :: Event t GameState, which I can use with stepper to make the Behavior t GameState

So, My questions are, is my line of thinking correct? If not, could I be re-directed? If so, what would eGameState :: Event t GameState look like?

In response to the response below. When I considered accumB initially, I saw a type error in the making. Which is what happened when I tried your suggestion.

let bGameState :: Behavior t GameState
    bGameState = accumB initialGS $ updateGS <$ eInput

yields the error

 Couldn't match expected type `GameState'
             with actual type `PlayerCommand'
 Expected type: GameState -> GameState
   Actual type: PlayerCommand -> GameState -> GameState
 In the first argument of `(<$)', namely `updateGS'
 In the second argument of `($)', namely `updateGS <$ eInput'

Not sure what to do about that. I'll look at your examples and see if the answer becomes clear. Thanks for poiting out accumB was the right way to go, as I was focused on stepper

The more I study the suggested code, the more I am puzzled by the type error.

回答1:

Indeed, you need to create an event which remembers a GameState and applies the updateGS function to it to create a new one. That's the purpose of the function accumE and its cousin accumB. In particular, you can write

bGameState = accumB initialGS $ updateGS <$> eInput

To learn more about this pattern, have a look at the examples on the examples pages, in particular the Counter.hs and TwoCounters.hs examples.


Another point worth mentioning is that I recommend to avoid the changes function unless you are dealing with low-level framework stuff. As noted in the documentation, it has several restrictions; the nastiest restriction being that the value is not available until the reactimate are executed. You can easily make an infinite loop that way, its purpose is really very narrow.

In your case, the bCommand seems superfluous anyway, you have eCommandChanged = eInput.

Morale: Turning an event into a behavior is easy, but there is no way back.