module Rasa.Internal.Async ( doAsync , eventProvider ) where import Rasa.Internal.Action import Rasa.Internal.Scheduler import Data.Typeable import Control.Concurrent.Async import Control.Lens import Control.Monad.IO.Class -- | This function takes an IO which results in some Event, it runs the IO -- asynchronously and dispatches the event, then repeats the process all over -- again. Use this inside the onInit scheduler to register an event listener -- for some event (e.g. keypresses or network activity) eventProvider :: Typeable a => IO a -> Action () eventProvider getEventIO = doAsync (dispatchAndRerun <$> getEventIO) where dispatchAndRerun evt = do dispatchEvent evt eventProvider getEventIO -- | doAsync allows you to perform a task asynchronously and then apply the -- result. In @doAsync asyncAction@, @asyncAction@ is an IO which resolves to -- an Action, note that the context in which the second action is executed is -- NOT the same context in which doAsync is called; it is likely that text and -- other state have changed while the IO executed, so it's a good idea to check -- (inside the applying Action) that things are in a good state before making -- changes. Here's an example: -- -- > asyncCapitalize :: Action () -- > asyncCapitalize = do -- > txt <- focusDo $ use text -- > -- We give doAsync an IO which resolves in an action -- > doAsync $ ioPart txt -- > -- > ioPart :: Text -> IO (Action ()) -- > ioPart txt = do -- > result <- longAsyncronousCapitalizationProgram txt -- > -- Note that this returns an Action, but it's still wrapped in IO -- > return $ maybeApplyResult txt result -- > -- > maybeApplyResult :: Text -> Text -> Action () -- > maybeApplyResult oldTxt capitalized = do -- > -- We get the current buffer's text, which may have changed since we started -- > newTxt <- focusDo (use text) -- > if newTxt == oldTxt -- > -- If the text is the same as it was, we can apply the transformation -- > then focusDo (text .= capitalized) -- > -- Otherwise we can choose to re-queue the whole action and try again -- > -- Or we could just give up. -- > else asyncCapitalize doAsync :: IO (Action ()) -> Action () doAsync asyncIO = do newAsync <- liftIO $ async asyncIO asyncs <>= [newAsync]