CollectionB - generic behavioral collections. \begin{code} module CollectionB where import FranCore import MapG import BVar import IOExts \end{code} A generic behavioral collection is defined as a CollectionB. This is a combination of an event giving incremental updates, and a behavior that represents the value of the collection over time. It is therefore parameterised over the update operation, the type of collection, and the type of values in the collection. \begin{code} data CollectionB b c a = CollectionB {eventC :: Event (b a), behaviorC :: Behavior (c a)} \end{code} We define a type class for collection updates. This allows a number of generic collection operations to be defined. We should be able to update a collection based on an update value (using applyOp) and reset the collection using (resetOp). \begin{code} class CollectionOp b c where applyOp :: b a -> c a -> c a resetOp :: c a -> b a \end{code} We make a collection by accumulating with an initial static collection ls, and updates based on the update event. \begin{code} newCollectionB :: CollectionOp b c => Event (b a) -> c a -> IO (CollectionB b c a) newCollectionB e ls = do b <- stepAccum ls (e ==> applyOp) return $! CollectionB e b \end{code} We can merge collections given a merge function to merge two static collections, and a newly merged update event. \begin{code} mergeCB :: CollectionB b c a -> CollectionB b c a -> Event (b a) -> (c a -> c a -> c a) -> CollectionB b c a mergeCB c1 c2 mergeE mergeC = CollectionB {eventC = mergeE, behaviorC = lift2 mergeC (behaviorC c1) (behaviorC c2)} \end{code} Mapping functions over behavior collections. \begin{code} instance (Functor c,Functor b) => Functor (CollectionB b c) where fmap = mapC instance (MapG e a b IO,MapG c a b IO,CollectionOp e c) => MapG (CollectionB e c) a b IO where mapG = mapIOC \end{code} To map a normal function over an event we map over the update event and the behavior. This will be inefficient if the map function is particularly complex and both the update event and behavior are used a lot. But it seems to work ok, for most examples I have tried. \begin{code} mapC :: (Functor c, Functor b) => (a -> d) -> CollectionB b c a -> CollectionB b c d mapC f (CollectionB e c) = CollectionB (e ==> fmap f) (lift1 (fmap f) c) \end{code} To map an IO action over a collection. We make a new collection formed by sampling the current value of the collection, mapping the IO action over the update event, and then generating a new collection. This has the alternative efficiency problem that we are ignoring the behavior bh, but the weak pointer stuff should generally cause bh to be garbage collected, and so stop the unnecessary work being done for it. \begin{code} mapIOC :: (MapG e a b IO,MapG c a b IO,CollectionOp e c) => (a -> IO b) -> CollectionB e c a -> IO (CollectionB e c b) mapIOC f (CollectionB e bh) = do t <- getFranTime vs <- bh `at` t init <- mapG f vs e' <- return $! withIOE e (mapG f) newCollectionB e' init \end{code} To make a collection switcher, that changes between collections received on an event. We start out with the current collection and then make the update event using switcherE. When the collection changes we sample the new collection, and send a reset operation. \begin{code} instance CollectionOp b c => SwitchableM (CollectionB b c a) IO where mkSwitcher = switcherC switcherC :: CollectionOp b c => (CollectionB b c a) -> Event (CollectionB b c a) -> IO (CollectionB b c a) switcherC c1 e = do t <- getFranTime s <- behaviorC c1 `at` t (Wire l ev) <- newWire addListener (eventC c1 `switcherE'` e ==> eventC) l addListener e (comapIOL (\c -> do t <- getFranTime vs <- behaviorC c `at` t return $ resetOp vs) l) newCollectionB ev s \end{code}