1

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?

jakubdaniel
  • 2,233
  • 1
  • 13
  • 20

1 Answers1

1

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

Community
  • 1
  • 1
Heinrich Apfelmus
  • 11,034
  • 1
  • 39
  • 67
  • I have never done anything regarding concurrent in functional languages, let alone Haskell, the links seem very interesting and I like your blog. Would it be too much to ask whether you knew about some nice comprehensive but easy to follow introduction into the matter that your forklift blog post builds upon? Either way, thank you for a very good pointers. – jakubdaniel Oct 13 '15 at 11:08
  • Also is there a deeper reason why this should not be possible without forking? What is the status of your forklift library? Btw both the links point to the same article, is that intentional? – jakubdaniel Oct 13 '15 at 12:30
  • @jd823592 Oops, sorry about the identical link, the first one was supposed to go somewhere completely different. – Heinrich Apfelmus Oct 13 '15 at 14:00
  • @jd823592 For an introduction to concurrent programming in Haskell, I recommend ["Tackling the awkward squad"](http://research.microsoft.com/en-us/um/people/simonpj/papers/marktoberdorf/) and ["Parallel and concurrent programming in Haskell"](http://community.haskell.org/~simonmar/pcph/) – Heinrich Apfelmus Oct 13 '15 at 14:02
  • Thank you! What exactly is blocking behaviour to fold over an arbitrary monad (`Interpreter` in my case)? – jakubdaniel Oct 13 '15 at 14:04
  • Especially, when sending messages requires `IO` thus the events cannot be pure anyway. I am confused :) – jakubdaniel Oct 13 '15 at 14:23
  • @jd823592 Well, the type of `reactimate` is `reactimate :: Event (IO a) -> MomentIO ()`, so the event may only contain an IO action to be run, not an `Interpreter` action. The deeper reason is that arbitrary monads may have nasty effects, like non-determinsm, and I'm not sure whether it's possible to maintain internal invariants for such cases. Essentially, the whole thing is tied to the type for event handlers, `type Handler a = a -> IO ()`. – Heinrich Apfelmus Oct 14 '15 at 09:34
  • Let us [continue this discussion in chat](http://chat.stackoverflow.com/rooms/92267/discussion-between-jd823592-and-heinrich-apfelmus). – jakubdaniel Oct 14 '15 at 13:11