Reactive Banana 1.0.0 - Why is this old code breaking? - haskell

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.

Related

How to add Mask to a monadic stack

I am trying to use the bracket function from Exception.Safe Package which has a return type of
forall m a b c. MonadMask m => m a -> (a -> m b) -> (a -> m c) -> m c
This implies that my monad stack must have the Mask monad added to the stack ?
My calling function looks like this
sendMessage :: String -> Config.KafkaP (Either KafkaError ())
sendMessage msg=do
getProperties <- producerProps
let
mkProducer = newProducer getProperties
clProducer (Left _) = return ()
clProducer (Right prod) = closeProducer prod
runHandler (Left err) = return $ Left err
runHandler (Right prod) = messageSender prod msg
res <- bracket mkProducer clProducer runHandler
return res
And config.KakfaP has the type
ReaderT ProducerConfig IO a
And the error I get is ,
No instance for (exceptions-0.10.0:Control.Monad.Catch.MonadMask
Config.KafkaP)
arising from a use of ‘bracket’
Does this mean the monad stack needs to be something like this
Mask (ReaderT ProducerConfig IO a)
Ideally I would want the function to return what the run Handler returns which is Config.KafkaP (Either KafkaError ()) ,or anything more robust .
Adding a solution based on an answer
sendMessage :: String -> Config.KafkaP (Either KafkaError ())
sendMessage msg=do
getProperties <- producerProps
let
mkProducer = newProducer getProperties
--newProducer :: MonadIO m => ProducerProperties -> m (Either KafkaError KafkaProducer)
--mkProducer :: Config.KafkaP (Either KafkaError KafkaProducer)
clProducer (Left _) = return ()
clProducer (Right prod) = closeProducer prod
--closeProducer :: MonadIO m => KafkaProducer -> m ()
--clProducer :: Config.KafkaP (Either () ()) -- ??
runHandler (Left err) = return $ Left err
runHandler (Right prod) = messageSender prod msg
--messageSender :: KafkaProducer -> String -> Config.KafkaP (Either KafkaError ())
--runHandler :: Config.KafkaP (Either KafkaError ()) -- ??
Config.KafkaP $ bracket (Config.runK mkProducer) (Config.runK .clProducer) (Config.runK .runHandler)
If you were using the type ReaderT ProducerConfig IO a directly, there wouldn't be a problem, because the exceptions package provides an instance
MonadMask IO
That says you can use bracket with IO, and another instance
MonadMask m => MonadMask (ReaderT r m)
That says that if the base monad is an instance of MonadMask, then ReaderT over that monad is also an instance of MonadMask.
Notice that MonadMask is not a transformer that is part of the monad stack. Instead, it is a constraint that says "this monad stack supports masking / bracketing operations".
If you were using a type synonym like
type KafkaP a = ReaderT ProducerConfig IO a
there wouldn't be a problem either, because type synonyms don't create a new type, they just give an alias to an exiting one. One can still make use of all the existing typeclass instances for the type.
You mention in the comments that KafkaP is a newtype. A newtype is a cheap way of creating, well, a new type out of another. It's basically a constructor that holds a value of the original type.
And that's the problem. Because it is a new type, it doesn't share automatically all the typeclass instances of the old one. In fact, having different typeclass instances in newtypes is one of the main motivations for using newtypes!
What can be done? Well, supposing the newtype constructor is exported (sometimes they are hidden for purposes of encapsulation) you could unwrap the KafkaPs actions into ReaderT ProducerConfig IO a before sending them into bracket, and then re-wrap the result into KafkaP again. Some of the parameters are KafkaP-returning functions, so you'll probably need to throw in some function composition as well. Perhaps something like (assuming KafkaP is the name of the constructor, and runKafkaP the name of the corresponding accessor):
KafkaP $ bracket (runKafkaP mkProducer) (runKafkaP . clProducer) (runKafkaP . runHandler)
All this wrapping and unwrapping is tedious; sometimes using coerce from Data.Coerce can help. But this only works when the newtype constructor is exported.
You might also think of defining your own MonadMask instance for KafkaP using the technique above. This can be done, but instances which are defined neither in the module that defines the typeclass nor in the module that defines the type are called orphan instances and are somewhat frowned upon.

Reactive Banana: consume parametrized call to an external API

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.

Dynamically updating UI based on previous updates

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.)

Is there an issue with an Event and a Behavior having the same initial value?

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.

Functional Banana Traveller - putting together Behavior t GameState

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.

Resources