reactive-banana-0.8.0.0: src/Control/Event/Handler.hs
module Control.Event.Handler (
-- * Synopsis
-- | <http://en.wikipedia.org/wiki/Event-driven_programming Event-driven programming>
-- in the traditional imperative style.
-- * Documentation
Handler, AddHandler(..), newAddHandler,
mapIO, filterIO,
) where
import Data.IORef
import qualified Data.Map as Map
import qualified Data.Unique
type Map = Map.Map
{-----------------------------------------------------------------------------
Types
------------------------------------------------------------------------------}
-- | An /event handler/ is a function that takes an
-- /event value/ and performs some computation.
type Handler a = a -> IO ()
-- | A value of type @Addhandler a@ is a facility for registering
-- event handlers. These will be called whenever the event occurs.
--
-- When registering an event handler, you will also be given an action
-- that unregisters this handler again.
--
-- > do unregisterMyHandler <- register addHandler myHandler
--
newtype AddHandler a = AddHandler { register :: Handler a -> IO (IO ()) }
{-----------------------------------------------------------------------------
Combinators
------------------------------------------------------------------------------}
instance Functor AddHandler where
fmap f = mapIO (return . f)
-- | Map the event value with an 'IO' action.
mapIO :: (a -> IO b) -> AddHandler a -> AddHandler b
mapIO f e = AddHandler $ \h -> register e $ \x -> f x >>= h
-- | Filter event values that don't return 'True'.
filterIO :: (a -> IO Bool) -> AddHandler a -> AddHandler a
filterIO f e = AddHandler $ \h ->
register e $ \x -> f x >>= \b -> if b then h x else return ()
{-----------------------------------------------------------------------------
Construction
------------------------------------------------------------------------------}
-- | Build a facility to register and unregister event handlers.
-- Also yields a function that takes an event handler and runs all the registered
-- handlers.
--
-- Example:
--
-- > do
-- > (addHandler, fire) <- newAddHandler
-- > register addHandler putStrLn
-- > fire "Hello!"
newAddHandler :: IO (AddHandler a, Handler a)
newAddHandler = do
handlers <- newIORef Map.empty
let register handler = do
key <- Data.Unique.newUnique
atomicModifyIORef_ handlers $ Map.insert key handler
return $ atomicModifyIORef_ handlers $ Map.delete key
runHandlers a =
mapM_ ($ a) . map snd . Map.toList =<< readIORef handlers
return (AddHandler register, runHandlers)
atomicModifyIORef_ ref f = atomicModifyIORef ref $ \x -> (f x, ())