{- - ``Data/Handler'' - (c) 2008 Cook, J. MR SSD, Inc. -} {-# LANGUAGE GeneralizedNewtypeDeriving, ExistentialQuantification, FlexibleContexts #-} module Data.Handler ( HandlerSet , HandlerID , emptyHandlerSet , emptyHandlerSetWithSequencer , setHandlerSequencer , addHandlerToSet , removeHandlerFromSet , invokeHandlers ) where -- generic "handler" concept. a handler has: -- at type level: -- an argument type -- a monad in which the handlers will run -- a return type -- at data level: -- a numeric ID within the context of the set of handlers registered for a given event -- a piece of code to run when triggered -- I would also like to add a description field for each handler at some point, -- and maybe also one for the whole handler set. import qualified Data.Map as M -- |An opaque identifier for handlers, useful for removing them from a set -- later. newtype HandlerID = HandlerID Int deriving (Eq, Ord, Enum) -- |A collection of handlers that can be fired in a batch. -- Consists of a collection of handlers and a sequencing rule that -- determines how multiple handlers are ordered and their results -- threaded or aggregated. data HandlerSet m a b = HandlerSet { handlerSet :: M.Map HandlerID (a -> m b) , handlerSequencer :: a -> [a -> m b] -> m b , nextHandlerId :: HandlerID } ---------------------------------------------------------------- -- some canned handler sequencing rules -- |Make a simple handler sequencer that executes handlers sequentially -- and returns whatever the last one returns (or the first 'b' parameter -- passed to 'sequenceHandlers' if there are no handlers in the set) sequenceHandlers :: Monad m => b -> a -> [a -> m b] -> m b sequenceHandlers defReturn a = sh where sh [] = return defReturn sh [x] = x a sh (x:xs) = x a >> sh xs ---------------------------------------------------------------- -- handler sets -- |Create a new handler set with a simple default sequencer -- ('sequenceHandlers' ()) emptyHandlerSet :: Monad m => HandlerSet m a () emptyHandlerSet = emptyHandlerSetWithSequencer (sequenceHandlers ()) -- |Create a new handler set using the provided function to coordinate -- the dispatch of the handlers emptyHandlerSetWithSequencer :: (a -> [a -> m b] -> m b) -> HandlerSet m a b emptyHandlerSetWithSequencer sequencer = HandlerSet { handlerSet = M.empty , handlerSequencer = sequencer , nextHandlerId = HandlerID 0 } -- |Add a handler to a set and return the updated set and the assigned 'HandlerID' addHandlerToSet :: (a -> m b) -> HandlerSet m a b -> (HandlerSet m a b, HandlerID) addHandlerToSet handler hs@(HandlerSet {handlerSet = hSet, nextHandlerId = hId}) = ( hs { handlerSet = M.insert hId handler hSet , nextHandlerId = succ hId } , hId ) -- |Attempt to remove a handler from a set (based on its 'HandlerID'), returning -- the modified set and the handler removed, if any. removeHandlerFromSet :: HandlerID -> HandlerSet m a b -> (HandlerSet m a b, Maybe (a -> m b)) removeHandlerFromSet hId hs@(HandlerSet {handlerSet = hSet}) = ( hs { handlerSet = rest } , maybeHandler ) where (matches, rest) = M.partitionWithKey (\k v -> k == hId) hSet maybeHandler = M.lookup hId matches -- |Replace the handler sequencing rule in a 'HandlerSet'. setHandlerSequencer :: (a -> [a -> m b] -> m b) -> HandlerSet m a b -> HandlerSet m a b setHandlerSequencer sequencer hs = hs { handlerSequencer = sequencer } -- |Invoke the handlers in a 'HandlerSet' with the provided input. invokeHandlers :: Monad m => HandlerSet m a b -> a -> m b invokeHandlers (HandlerSet {handlerSet = handlers, handlerSequencer = sequencer}) args = do sequencer args (M.elems handlers)