{-# language FlexibleContexts, TypeFamilies #-} module Main where {- | * Multithreaded Multi-Host Example This example boots two reflex networks which we will call "Left" and "Right". As they start, they each write an event trigger into an 'MVar' and read an event trigger from another 'MVar', so that they can fire events into each other's network. Left and Right both log to stdout any lines received from each other. Left also reads lines from stdin, and lines are passed around as follows: Left passes incoming lines to Right, which immediately sends received lines back to Left. If "quit" is read from stdin, this triggers a shutdown of both FRP networks. -} import Control.Concurrent (forkIO) import Control.Concurrent.MVar (MVar, newEmptyMVar, readMVar, putMVar, takeMVar) import Control.Lens ((<&>)) import Control.Monad (forever) import Control.Monad.IO.Class (liftIO) import Data.Functor (($>), void) import Data.Semigroup ((<>)) import Data.Witherable (filter) import Prelude hiding (filter) import Reflex import Reflex.Host.Basic left :: BasicGuestConstraints t m => MVar (String -> IO ()) -> MVar (String -> IO ()) -> BasicGuest t m (Event t ()) left mTriggerLeft mTriggerRight = do -- Construct the event "lines from Right", and put its trigger in an MVar. (eFromRight, triggerLeft) <- newTriggerEvent liftIO $ putMVar mTriggerLeft triggerLeft -- Kick off a loop to read from stdin. Ignore exceptions for brevity. ePostBuild <- getPostBuild eLines <- performEventAsync $ ePostBuild $> \fire -> void . liftIO . forkIO . forever $ getLine >>= fire -- Get the event trigger for the "lines from Left" event inside the -- Right network, and fire it on each line. triggerRight <- liftIO $ readMVar mTriggerRight performEvent_ $ liftIO . triggerRight <$> eLines -- Log events received from Right. performEvent_ $ eFromRight <&> \msg -> liftIO . putStrLn $ "From Right: " <> msg -- Quit if we get a "quit" from Right. pure . void $ filter (== "quit") eFromRight right :: BasicGuestConstraints t m => MVar (String -> IO ()) -> MVar (String -> IO ()) -> BasicGuest t m (Event t ()) right mTriggerLeft mTriggerRight = do -- Construct the event "lines from Left", and put its trigger in an MVar. (eFromLeft, triggerRight) <- newTriggerEvent liftIO $ putMVar mTriggerRight triggerRight -- Get the event trigger for the "lines from Right" event inside the -- Left network, and fire it on each line. triggerLeft <- liftIO $ readMVar mTriggerLeft performEvent_ $ liftIO . triggerLeft <$> eFromLeft -- Log events received from Left. performEvent_ $ eFromLeft <&> \msg -> liftIO . putStrLn $ "From Left: " <> msg -- Quit if we get a "quit" from Left. pure . void $ filter (== "quit") eFromLeft main :: IO () main = do -- Removing these type annotations causes type errors like "a0 is -- untouchable". mTriggerLeft <- newEmptyMVar :: IO (MVar (String -> IO ())) mTriggerRight <- newEmptyMVar :: IO (MVar (String -> IO ())) mLeftDone <- newEmptyMVar mRightDone <- newEmptyMVar -- Not exception-safe, for brevity's sake. void . forkIO $ do basicHostWithQuit (left mTriggerLeft mTriggerRight) putMVar mLeftDone () void . forkIO $ do basicHostWithQuit (right mTriggerLeft mTriggerRight) putMVar mRightDone () -- Wait for both threads takeMVar mLeftDone takeMVar mRightDone