multithreadinghaskellreactive-banana

Events from shared communication channel between threads


I have a program that solves some problem and I decided I would like to monitor in a nice GUI what it does. For GUI I chose Gtk which means that I need to run mainGUI loop in a dedicated thread and the rest of my program will occupy a different thread. I thought the communication between my program and the other thread would flow in one direction using Chan. I further decided to use FRP for updating the GUI on notification from the worker (the original program's logic in its separate thread). So I tried writing a simple threaded example where one thread sends IO actions to the monitoring thread which performs the actions (displays them). Here is my attempt:

import Control.Concurrent
import Control.Monad

import Reactive.Banana
import Reactive.Banana.Frameworks

main = do
    c <- newChan

    forkIO $ do
        actuate <=< compile $ reactimate'
                            <=< changes
                            <=< fromPoll
                            $ readChan c

    forever $ do
        threadDelay 3000000
        putStrLn "sending msg"
        writeChan c $ putStrLn "receiving msg"

This obviously doesn't work (it only prints sending msg) otherwise I wouldn't be here. What am I doing wrong? Do I need a different event that times the polling? How to do it?

I expected some interleaving of copies of the texts: sending msg and receiving msg.


To clarify I want to move from

main = do
    c <- newChan

    forkIO . forever . join . readChan $ c

    forever $ do
        threadDelay 3000000
        putStrLn "sending msg"
        writeChan c $ putStrLn "receiving msg"

where each message in c :: Chan (IO ()) is read in the thread explicitly (possibly blocking), to reactive handling of the messages, i.e. describing a network of events/behaviours interconnected with GUI elements and then let the thread perform GUI loop. The network would need to take care of polling values in the channel and firing events.


The solution I was seeking (or something like it):

main = do
    (msgHandler, msgFire) <- newAddHandler

    forkIO $ do
        actuate <=< compile $ do
            eMsg <- fromAddHandler msgHandler

            reactimate $ putStrLn <$> eMsg

    forever $ do
        threadDelay 3000000
        putStrLn "sending msg"
        msgFire "receiving msg"

Solution

  • The documentation for fromPoll states

    The resulting Behavior will be updated on whenever the event network processes an input event.

    Since there is no event, it is never updated. fromPoll is intented as a quick-and-dirty way to read mutable data, but not to update the network. Rather, the documentation recommends we use fromChanges. But since we or, because we want Event anyway, let's use newEvent, which seems quite appropriate: it allows us to create an Event to which we add values by calling a Handler (which is an alias for a -> IO ()).

    import Control.Concurrent
    import Control.Monad
    
    import Reactive.Banana
    import Reactive.Banana.Frameworks
    
    main = do
    
        c <- newChan
    
        network <- compile $ do
          (event, handler) <- newEvent
          liftIO $ forkIO $ forever (readChan c >>= handler)
          reactimate event
    
        forkIO $ actuate network
    
        forever $ do
            threadDelay 3000000
            putStrLn "sending msg"
            writeChan c $ putStrLn "receiving msg"