{-# LANGUAGE GeneralizedNewtypeDeriving #-} module Updater ( -- * Signals Signal(), newSignal, newSignalIO, writeSignal, readSignal, -- addListener, -- * Updater Monad Updater(), runUpdater, getEvent, onCommit, onCleanup, -- * Helpers stop, modifySignal, getBehavior, local, liftSTM, putLine, runGlobalUpdater ) where import Control.Concurrent import Control.Applicative import Updater.Internal hiding (newSignal, readSignal) import qualified Updater.Internal as Internal import System.IO.Unsafe -- | -- Creates a new signal. You can use this signal in any -- context you want and share it freely between any -- number of different Updater monads. newSignal :: a -> Updater (Signal a) newSignal = liftSTM . Internal.newSignal -- | -- Just a synonym for `empty` from `Alternative`. -- It basically prevents signals from ever progressing beyond this point. -- You can use this to make a filter for instance -- -- >when (condition) stop stop :: Updater a stop = empty -- | -- Just for some quick debugging -- -- >putLine = onCommit . putStrLn putLine :: String -> Updater () putLine = onCommit . putStrLn -- | -- Returns immediately after registering the given computation. -- However, events from inside will not spread outside, except for -- the initial one. -- -- It is implemented like this -- -- >local computation = return () <|> (computation >> stop) local :: Updater a -> Updater () local computation = return () <|> (computation >> stop) -- | -- Gets the current value. readSignal :: Signal a -> Updater a readSignal = liftSTM . Internal.readSignal -- | -- simple combination of readSignal and writeSignal modifySignal :: Signal a -> (a -> a) -> Updater () modifySignal s f = readSignal s >>= writeSignal s . f -- | -- this is just a convenience for use in ghci -- and in the test cases. It will just run -- the updater it is given in it's own thread. runGlobalUpdater :: Updater a -> IO () runGlobalUpdater u = runUpdater $ writeSignal globalUpdater (u >> return ()) globalUpdater :: Signal (Updater ()) {-# NOINLINE globalUpdater #-} globalUpdater = unsafePerformIO $ do s <- newSignalIO $ return () forkIO $ runUpdater $ do currentUpdater <-getBehavior s currentUpdater stop return s