Suppose I catch key presses and manipulate a code buffer accordingly:
let
bCode = accumB emptyCode eModifications
eCodeChanges <- changes bCode
I would like to create another behaviour bEval
bEval = accumB freshEnv (magic eCodeChanges)
which maps any state of code to its evaluation (triggered only when something really changes).
However, evaluation happens in a monad Interpreter
(think hint
from hackage). Can I actually define such a behaviour bEval
? I guess I could just drag Interpreter String
as the state in my behaviour, accumulating with currentAccumState >>= eval nextEvent
, but where would I runInterpreter
to actually force the evaluation?
Edit: Important thing is that the actions are not merely IO ()
but are supposed to modify some state. Consider for example clearing a buffer/reseting counter/moving around a zipper.
My idea was along the lines:
f :: a -> Maybe (b -> IO b)
mapJustIO :: (a -> Maybe (b -> IO b)) -> Event t a -> Event t (b -> IO b)
mapJustIO f e = filterJust $ f <$> e
accumIO :: a -> Event t (a -> IO a) -> Behaviour t (IO a)
accumIO z e = fold (>>=) (return z) e
I don't see why there could not be something like this. However, I don't see how to get rid of that IO
in the behaviour either :).
Why aren't IO
occurrences in reactive-banana
actually only MonadIO
?
The short answer is that combinators like accumB
and so on can only work with pure functions. They can't work with functions from the IO monad (or another IO-like monad like Interpreter
), because there is no way to define the order of actions in any meaningful way.
To work with IO actions, the combinators from Reactive.Banana.Frameworks
are appropriate. For instance, you probably want a function like
mapIO' :: (a -> IO b) -> Event a -> MomentIO (Event b)
See a previous answer for more on this.
In the case of the Interpreter
monad from hint
, I recommend running it in the main thread, forking a separate thread for your FRP logic, and using TVar
s or TChan
for communication between the two. (I like to calls this the forklift pattern). This way, you can access the Interpreter
monad from IO
.