问题
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.
来源:https://stackoverflow.com/questions/13035655/functional-banana-traveller-putting-together-behavior-t-gamestate