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"
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"