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.
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.
Related
Here's code that used to work (truncated appropriately I hope)
makeNetworkDescription :: forall t . Frameworks t => Parameters -> Moment t ()
makeNetworkDescription params = do
eInput <- fromAddHandler (input params)
eTick <- fromAddHandler (tick params)
..
let
bResourceMap :: Behavior t ResourceMap
bResourceMap = accumB initRmap $
adjustMarket <$>
bMarketRolls <#
eTick
But now the types have changed.
we have:
makeNetworkDescription :: Parameters -> MomentIO ()
and
accumB :: MonadMoment m => a -> Event (a -> a) -> m (Behavior a)
say I change the definition of bResourceMap to
bResourceMap :: Behavior ResourceMap
bResourceMap = accumB initRmap $
adjustMarket <$>
bMarketRolls <#
eTick
slightly off from the accumB definition, but let's see what happens.
ghc gives an error
Couldn't match type ‘Behavior ResourceMap’ with ‘ResourceMap’
Expected type: Behavior ResourceMap
Actual type: Behavior (Behavior ResourceMap)
Right, because of the type of accumB the behavior needs to be within the context of a MonadMoment. Having a look at MonadMoment I find two instances
instance MonadMoment Moment where liftMoment = id
instance MonadMoment MomentIO where liftMoment = MIO . unM
So why did the actual type resolve to Behavior (Behavior ResourceMap), the outer type has to be a MonadMoment, which doesn't match.
I'd like advice on how to resolve this type of problem, it happens with all my Behavior definitions.
Adjusting your code to fit the new type of accumB should only take using a monadic bind rather than a let expression to define bResourceMap:
bResourceMap <- accumB initRmap (adjustMarket <$> bMarketRolls <# eTick)
The type error you quote seems unrelated. My guess would be that initRmap was accidentally changed from being a ResourceMap to a Behavior ResourceMap, leading to the the type mismatch.
Starting from a previous question here:
Reactive Banana: how to use values from a remote API and merge them in the event stream
I have a bit different problem now: How can I use the Behaviour output as input for an IO operation and finally display the IO operation's result?
Below is the code from the previous answer changed with a second output:
import System.Random
type RemoteValue = Int
-- generate a random value within [0, 10)
getRemoteApiValue :: IO RemoteValue
getRemoteApiValue = (`mod` 10) <$> randomIO
getAnotherRemoteApiValue :: AppState -> IO RemoteValue
getAnotherRemoteApiValue state = (`mod` 10) <$> randomIO + count state
data AppState = AppState { count :: Int } deriving Show
transformState :: RemoteValue -> AppState -> AppState
transformState v (AppState x) = AppState $ x + v
main :: IO ()
main = start $ do
f <- frame [text := "AppState"]
myButton <- button f [text := "Go"]
output <- staticText f []
output2 <- staticText f []
set f [layout := minsize (sz 300 200)
$ margin 10
$ column 5 [widget myButton, widget output, widget output2]]
let networkDescription :: forall t. Frameworks t => Moment t ()
networkDescription = do
ebt <- event0 myButton command
remoteValueB <- fromPoll getRemoteApiValue
myRemoteValue <- changes remoteValueB
let
events = transformState <$> remoteValueB <# ebt
coreOfTheApp :: Behavior t AppState
coreOfTheApp = accumB (AppState 0) events
sink output [text :== show <$> coreOfTheApp]
sink output2 [text :== show <$> reactimate ( getAnotherRemoteApiValue <#> coreOfTheApp)]
network <- compile networkDescription
actuate network
As you can see what I am trying to do it is using the new state of the application -> getAnotherRemoteApiValue -> show. But it doesn't work.
Is actually possible doing that?
UPDATE
Based on the Erik Allik and Heinrich Apfelmus below answers I have the current code situation - that works :) :
{-# LANGUAGE ScopedTypeVariables #-}
module Main where
import System.Random
import Graphics.UI.WX hiding (Event, newEvent)
import Reactive.Banana
import Reactive.Banana.WX
data AppState = AppState { count :: Int } deriving Show
initialState :: AppState
initialState = AppState 0
transformState :: RemoteValue -> AppState -> AppState
transformState v (AppState x) = AppState $ x + v
type RemoteValue = Int
main :: IO ()
main = start $ do
f <- frame [text := "AppState"]
myButton <- button f [text := "Go"]
output1 <- staticText f []
output2 <- staticText f []
set f [layout := minsize (sz 300 200)
$ margin 10
$ column 5 [widget myButton, widget output1, widget output2]]
let networkDescription :: forall t. Frameworks t => Moment t ()
networkDescription = do
ebt <- event0 myButton command
remoteValue1B <- fromPoll getRemoteApiValue
let remoteValue1E = remoteValue1B <# ebt
appStateE = accumE initialState $ transformState <$> remoteValue1E
appStateB = stepper initialState appStateE
mapIO' :: (a -> IO b) -> Event t a -> Moment t (Event t b)
mapIO' ioFunc e1 = do
(e2, handler) <- newEvent
reactimate $ (\a -> ioFunc a >>= handler) <$> e1
return e2
remoteValue2E <- mapIO' getAnotherRemoteApiValue appStateE
let remoteValue2B = stepper Nothing $ Just <$> remoteValue2E
sink output1 [text :== show <$> appStateB]
sink output2 [text :== show <$> remoteValue2B]
network <- compile networkDescription
actuate network
getRemoteApiValue :: IO RemoteValue
getRemoteApiValue = do
putStrLn "getRemoteApiValue"
(`mod` 10) <$> randomIO
getAnotherRemoteApiValue :: AppState -> IO RemoteValue
getAnotherRemoteApiValue state = do
putStrLn $ "getAnotherRemoteApiValue: state = " ++ show state
return $ count state
The fundamental problem is a conceptual one: FRP Events and Behaviors can only be combined in a pure way. In principle, it is not possible to have a function of type, say
mapIO' :: (a -> IO b) -> Event a -> Event b
because the order in which the corresponding IO actions are to be executed is undefined.
In practice, it may sometimes be useful to perform IO while combining Events and Behaviors. The execute combinator can do this, as #ErikAllik indicates. Depending on the nature of getAnotherRemoteApiValue, this may be the right thing to do, in particular if this is function is idempotent, or does a quick lookup from location in RAM.
However, if the computation is more involved, then it is probably better to use reactimate to perform the IO computation. Using newEvent to create an AddHandler, we can give an implementation of the mapIO' function:
mapIO' :: (a -> IO b) -> Event a -> MomentIO (Event b)
mapIO' f e1 = do
(e2, handler) <- newEvent
reactimate $ (\a -> f a >>= handler) <$> e1
return e2
The key difference to the pure combinator
fmap :: (a -> b) -> Event a -> Event b
is that the latter guarantees that the input and result events occur simultaneously, while the former gives absolutely no guarantee about when the result event occurs in relation to other events in the network.
Note that execute also guarantees that input and result are have simultaneous occurrences, but places informal restrictions on the IO allowed.
With this trick of combining reactimate with newEvent a similar combinator can be written for Behaviors in a similar fashion. Keep in mind that the toolbox from Reactive.Banana.Frameworks is only appropriate if you are dealing with IO actions whose precise order will necessarily be undefined.
(To keep this answer current, I have used the type signatures from the upcoming reactive-banana 1.0. In version 0.9, the type signature for mapIO' is
mapIO' :: Frameworks t => (a -> IO b) -> Event t a -> Moment t (Event t b)
)
TL;DR: scroll down to the ANSWER: section for a solution along with an explanation.
First of all
getAnotherRemoteApiValue state = (`mod` 10) <$> randomIO + count state
is invalid (i.e. does not typecheck) for reasons completely unrelated to FRP or reactive-banana: you cannot add an Int to an IO Int — just as you can't apply mod 10 to an IO Int directly, which is exactly why, in the answer to your original question, I used <$> (which is another name for fmap from Functor).
I strongly recommend you look up and understand the purpose/meaning of <$>, along with <*> and some other Functor and Applicative type class methods — FRP (at least the way it is designed in reactive-banana) builds heavily upon Functors and Applicatives (and sometimes Monads, Arrows and possibly some other more novel foundation), hence if you don't completely understand those, you won't ever become proficient with FRP.
Secondly, I'm not sure why you're using coreOfTheApp for sink output2 ... — the coreOfTheApp value is related to the other API value.
Thirdly, how should the other API value be displayed? Or, more specifically, when should it be displayed? Your first API value is displayed when the button is clicked but there's no button for the second one — do you want the same button to trigger the API call and display update? Do you want another button? Or do you want it to be polled every n unit of time and simply auto-updated in the UI?
Lastly, reactimate is meant for converting a Behavior into an IO action, which is not what you want, because you already have the show helper and don't need to setText or smth on the static label. In other words, what you need for the second API value is the same as before, except you need to pass something from the app state along with the request to the external API, but aside from that difference, you can still just keep showing the (other) API value using show as normal.
ANSWER:
As to how to convert getAnotherRemoteApiValue :: AppState -> IO RemoteValue into an Event t Int similar to the original remoteValueE:
I first tried to go via IORefs and using changes+reactimate', but that quickly turned out to a dead end (besides being ugly and overly complicated): output2 was always updated one FRP "cycle" too late, so it was always one "version" behind in the UI.
I then, with the help of Oliver Charles (ocharles) on #haskell-game on FreeNode, turned to execute:
execute :: Event t (FrameworksMoment a) -> Moment t (Event t a)
which I still don't fully grasp yet, but it works:
let x = fmap (\s -> FrameworksMoment $ liftIO $ getAnotherRemoteApiValue s)
(appStateB <# ebt)
remoteValue2E <- execute x
so the same button would trigger both actions. But the problem with that quickly turned out to be the same as with the IORef based solution — since the same button would trigger a pair of events, and one event inside that pair depended on the other, the contents of output2 was still one version behind.
I then realised the events relatede to output2 need to be triggered after any events related to output1. However, it's impossible to go from Behavior t a -> Event t a; in other words, once you have a behavior, you can't (easily?) obtain an event from that (except with changes, but changes is tied to reactimate/reactimate', which is not useful here).
I finally noticed that I was essentially "throwing away" an intermediate Event at this line:
appStateB = accumB initialState $ transformState <$> remoteValue1E
by replacing it with
appStateE = accumE initialState $ transformState <$> remoteValue1E
appStateB = stepper initialState -- there seems to be no way to eliminate the initialState duplication but that's fine
so I still had the exact same appStateB, which is used as previously, but I could then also rely on appStateE to reliably trigger further events that rely on the AppState:
let x = fmap (\s -> FrameworksMoment $ liftIO $ getAnotherRemoteApiValue s)
appStateE
remoteValue2E <- execute x
The final sink output2 line looks like:
sink output2 [text :== show <$> remoteValue2B]
All of the code can be seen at http://lpaste.net/142202, with debug output still enabled.
Note that the (\s -> FrameworkMoment $ liftIO $ getAnotherRemoteApiValue s) lambda cannot be converted to point-free style for reasons related to RankN types. I was told this problem will go away in reactive-banana 1.0 because there will be no FrameworkMoment helper type.
I'm doing some research into practical aspects of FRP for UI's and I've been struggling with implementing the following functionality using reactive banana: based on the value of a selection box, a variable amount of list boxes are rendered which display some results. (I'm using WxHaskell.)
It was pretty straightforward to implement this using a bunch of prepared list boxes that are hidden and shown based on the result behavior, but this time I want it to create and destroy list boxes as needed, each list box linked to the results behavior.
So far I have the following ingredients:
an event eParam which is bound to the selection box
a behavior bResults :: Behavior t [[String]] defined with eParam (and stepper) which holds all the results (lists of items per list box)
an update function updateResultControls :: [SingleListBox ()] -> [[String]] -> IO [SingleListBox ()] which destroys or builds the list boxes based on the results. Note that the return type is in IO.
Looking at the BarTab example, I've tried to implement the following:
a behavior bResultControls :: Behavior t [SingleListBox ()] with the list boxes, defined as stepper [] eUpdateResultControls.
an event eUpdateResultControls :: Event t [SingleListBox ()] that performs the UI update. This event depends on the behaviors bResultControls and bResults. However, it also has to update the network and run IO, so I suspect Moment and execute will be involved. This is where I got stuck.
My latest attempt is this:
rec
let
bResultControls = stepper [] eResultControls
bResultControlsUpdate = updateResultControls <$> bResultControls <*> bResults
eResultControls <- execute $ FrameworksMoment . liftIO <$> (bResultControlsUpdate <# eParam)
But I get the following type error:
Couldn't match type `m0 [SingleListBox ()]'
with `forall t1. Frameworks t1 => Moment t1 [SingleListBox ()]'
Expected type: IO [SingleListBox ()]
-> forall t. Frameworks t => Moment t [SingleListBox ()]
Actual type: IO [SingleListBox ()] -> m0 [SingleListBox ()]
In the second argument of `(.)', namely `liftIO'
In the first argument of `(<$>)', namely
`FrameworksMoment . liftIO'
In the second argument of `($)', namely
`FrameworksMoment . liftIO <$> (bResultControlsUpdate <# eParam)'
I suspect this will involve trimming some behaviors, or perhaps I'm going about this entirely the wrong way.
After some more reading and experimenting I got it to work with some careful trimming and refactoring (as hinted at by Heinrich):
networkDescription :: forall t. Frameworks t => Moment t ()
networkDescription = do
eParam <- choiceSelection cParam
let bResults = results <$> stepper x eParam
bResults_ <- trimB bResults
rec
let
bResultControls = stepper [] eResultControls
mkResultControls :: [SingleListBox ()] -> [[String]] -> FrameworksMoment [SingleListBox ()]
mkResultControls cs rs = FrameworksMoment $ do
slResults <- liftIO $ updateResultControls cs rs
bResults <- now bResults_
sequence_ [sink sl [items :== (!! i) <$> bResults] | sl <- slResults | i <- [0..]]
liftIO $ do
let n = length rs
set f [clientSize := sz (150 * n) 200]
set pResults [layout := fill $ boxed "results" $ row n (map (fill . widget) slResults)]
refit f
return slResults
eResultControls <- execute $ (mkResultControls <$> stepper [] eResultControls <*> bResults) <# eParam
return ()
(Just got a little bug now where the event fires before the behavior updates but that should be easy to fix.)
So I am trying to implement a Haskell game using the State and as a part of the game, I wanted to implement ways to save a current player's name and retrieve it when called. I have helper functions popStack and pushStack which pops and pushes values into stack respectively.
The current code:
import Control.Monad.State
data Gamestate = Gamestate {
gamestack :: [String],
gamememory :: String
}
type NewGameState = State GameState
popStack :: NewGameState String
popStack = state $ \st -> case gamestack st of
[] -> (0.0,st)
x:xs -> (x,st { gamestack = xs })
pushStack :: String -> NewGameState ()
push d = modify $ \st -> st { gamestack = d : gamestack st }
I have come up with the following code for saveName and getName.
saveName :: NewGameState ()
saveName = do
memory <-head
pushStack $ x
getName :: NewGameState ()
getName = do
memory <- head gamestack
popStack $ memory
The code snippets above return type errors. I don't understand State Monads much. So how do I copy the current players name at the top of the gamestack into gamememory using saveName and push the gamememory at the top of gamestack when I use getName?
Sorry if its a bit confusing. I am an ESL speaker. Thanks in advance.
I'm going to answer your question by showing you the idiomatic way to do what you are trying to do. As I go along I'm going to point out what I fixed in your code.
First problem: You have inconsistent capitalization of Gamestate. Capitalization matters in Haskell, so I renamed everything to GameState.
So after making that fixes, the first thing I did was define lenses to your two data type's fields. This makes it much easier do stateful things that modify a subset of your state. You will see this when I get to the implementations of the remaining functions:
import Control.Monad.State
import Control.Lens
data GameState = GameState
{ _gamestack :: [String]
, _gamememory :: String
}
gamestack :: Lens' GameState [String]
gamestack k (GameState s m) = fmap (\s' -> GameState s' m) (k s)
gamememory :: Lens' GameState String
gamememory k (GameState s m) = fmap (\m' -> GameState s m') (k m)
type NewGameState = State GameState
Note that you don't have to manually define lenses like this. Instead of defining gamememory and gamestack, you could also have done this instead:
{-# LANGUAGE TemplateHaskell #-} -- Note the extension
import Control.Lens
data GameState = GameState
{ _gamestack :: [String]
, _gamememory :: String
}
makeLenses ''GameState
Whichever way you choose, once we have these lenses, we can write push and pop in such a way that they don't care what state they are acting on, as long as it is a list:
pop :: State [a] (Maybe a)
pop = do
s <- get
case s of
[] -> return Nothing
x:xs -> do
put xs
return (Just x)
push :: a -> State [a] ()
push d = modify (d:)
Notice that I changed pop to return a Maybe if the list is empty. This is more idiomatic Haskell than defaulting to 0 or using head.
Using push and pop, it becomes very easy to transfer values between your game's memory and its stack:
saveName :: NewGameState ()
saveName = do
memory <- use gamememory
zoom gamestack (push memory)
getName :: NewGameState ()
getName = do
m <- zoom gamestack pop
case m of
Nothing -> return ()
Just x -> gamememory .= x
Notice how I use zoom to localize push and pop to operate on either the gamememory or the gamestack fields. zoom takes a lens to a sub-field and then runs the stateful action as if the entire state were just that sub-field. This is cool because now push and pop are much more reusable and we don't have to bake in a specific choice of state data type into them.
This also uses .=, which sets a given field. It's basically the same as:
lens .= x = zoom lens (put x)
To learn more about lenses, (.=), and zoom, you might want to read this post that I wrote.
Edit: By request, here is the lens-free version:
import Control.Monad.State
data GameState = GameState
{ gamestack :: [String]
, gamememory :: String
}
type NewGameState = State GameState
saveName :: NewGameState ()
saveName = do
GameState stack memory <- get
put (GameState (memory:stack) memory)
getName :: NewGameState ()
getName = do
GameState stack memory <- get
case stack of
[] -> put (GameState stack memory)
x:xs -> put (GameState xs x )
If something is on the right side of a <- than it has to be in that monad. So what you want here is something like
saveName :: NewGameState ()
saveName = do
memory <- fmap gamememory get
pushStack memory
getName = popStack
for saveName we fmap gamememory over the current state and store the result in memory than push that on the stack. We can actually write this as get >>= pushStack . gamememory if you want to be fancy.
popStack doesn't take any arguments so I'm not sure what you wanted there. My best guess is that it should just grab the last name we pushed on which just is a call to popStack.
NewGameState is a poor name - it isn't a new game state at all, it's a monad that carries around a state. I just called it Game.
pushStack vs push - You gave a signature named pushStack then a function named push. Pick one.
In popStack you have [] -> (0.0, st) Let's face it, 0.0 is not a string, so why are you trying to return it? Did you just not know what to do when popping an empty stack? How about you use "" instead?
saveName and getName Well you haven't even said what you want these to do. It seems you accepted other answerer's interpretations so going with that, we can just use record update syntax.
In the end, here is some code that at least compiles:
import Control.Monad.State
data GameState = GameState {
gamestack :: [String],
gamememory :: String
}
type Game = State GameState
popStack :: Game (Maybe String)
popStack = state $ \st -> case gamestack st of
[] -> (Nothing,st)
x:xs -> (Just x,st { gamestack = xs })
pushStack :: String -> Game ()
pushStack d = modify $ \st -> st { gamestack = d : gamestack st }
saveName :: Game ()
saveName = do
memory <- gamememory `fmap` get
pushStack memory
getName :: Game ()
getName = do
newMem <- popStack
case newMem of
Nothing -> return ()
Just n -> modify (\x -> x { gamememory = n } )
There's something I find unsatisfying with the below code. As I develop bGameState, I will add more events. Will the fact that playerInputE (and I imagine other Events) share the same initial value lead to problems? In other words, is my initial design sound enough to build off of?
Also, is there an alternative to using changes? I think I meet the criteria for correct use, but am not sure.
makeNetworkDescription :: AddHandler PlayerCommand ->
TChan GameState ->
IO EventNetwork
makeNetworkDescription addCommandEvent gsChannel = compile $ do
eInput <- fromAddHandler addCommandEvent
let playerInputE = accumE initialGS $ updateGS <$> eInput
bGameState = stepper initialGS playerInputE
eGameState <- changes bGameState
reactimate $ (\n -> (atomically $ writeTChan gsChannel n)) <$> eGameState
I do not quite understand what you are trying to do, but you can use the accumB combinator which is defined as
accumB x e = stepper x (accumE x e)
to remove the definition of playerInputE.
It appears to me that the changes function is used correctly.