packages feed

reactive-0.5: src/Data/EventExtras.hs

{-# OPTIONS_GHC -Wall #-}
----------------------------------------------------------------------
-- |
-- Module      :  Data.EventExtras
-- Copyright   :  (c) Conal Elliott 2008
-- License     :  BSD3
-- 
-- Maintainer  :  conal@conal.net
-- Stability   :  experimental
-- 
-- Event "extras", i.e., independent of representation
----------------------------------------------------------------------

module Data.EventExtras
  (
    module Data.SEvent
    -- * Event extras
  , EventD, EventI
  , traceE, pairE, scanlE, monoidE
  , withPrevE, countE, countE_, diffE
    -- * To be moved elsewhere
  , joinMaybes, filterMP
  ) where

import Control.Monad (liftM)
import Control.Applicative ((<$>),liftA2)
import Data.Pair (pairEdit)
import Data.Monoid
import Control.Monad (MonadPlus(..))
import Debug.Trace (trace)

import Data.SEvent
-- import Data.MEvent

import Data.Improving

-- | Event, using Double for time
type EventD = Event' Double

-- | Event, using an /improving/ double for time
type EventI = Event' (Improving Double)

-- | Tracing of events.
traceE :: (a -> String) -> Event' t a -> Event' t a
traceE shw = fmap (\ a -> trace (shw a) a)

pairE :: Ord t => (c,d) -> (Event' t c, Event' t d) -> Event' t (c,d)
pairE cd cde = cd `accumE` pairEdit cde

-- | Like 'scanl' for events.  See also 'scanlR'.
scanlE :: Ord t => (a -> b -> a) -> a -> Event' t b -> Event' t a
scanlE f a e = a `accumE` (flip f <$> e)

-- | Accumulate values from a monoid-valued event.  Specialization of
-- 'scanlE', using 'mappend' and 'mempty'.  See also 'monoidR'.
monoidE :: (Ord t, Monoid o) => Event' t o -> Event' t o
monoidE = scanlE mappend mempty

-- | Pair each event value with the previous one, given an initial value.
withPrevE :: Ord t => Event' t a -> Event' t (a,a)
withPrevE e = (joinMaybes . fmap combineMaybes) $
              (Nothing,Nothing) `accumE` fmap (shift.Just) e
 where
   -- Shift newer value into (old,new) pair if present.
   shift :: u -> (u,u) -> (u,u)
   shift new (_,old) = (old,new)
   combineMaybes :: (Maybe u, Maybe v) -> Maybe (u,v)
   combineMaybes = uncurry (liftA2 (,))

-- | Count occurrences of an event, remembering the occurrence values.
-- See also 'countE_'.
countE :: (Ord t, Num n) => Event' t b -> Event' t (b,n)
countE = scanlE h (b0,0)
 where
   b0        = error "withCountE: no initial value"
   h (_,n) b = (b,n+1)

-- | Count occurrences of an event, forgetting the occurrence values.  See
-- also 'countE'.  See also 'countR'.
countE_ :: (Ord t, Num n) => Event' t b -> Event' t n
countE_ e = snd <$> countE e

-- | Difference of successive event occurrences.
diffE :: (Ord t, Num n) => Event' t n -> Event' t n
diffE e = uncurry (-) <$> withPrevE e


{--------------------------------------------------------------------
    To be moved elsewhere
--------------------------------------------------------------------}

-- | Pass through @Just@ occurrences.
joinMaybes :: MonadPlus m => m (Maybe a) -> m a
joinMaybes = (>>= maybe mzero return)

-- | Pass through values satisfying @p@.
filterMP :: MonadPlus m => (a -> Bool) -> m a -> m a
filterMP p m = joinMaybes (liftM f m)
 where
   f a | p a       = Just a
       | otherwise = Nothing

-- Alternatively:
-- filterMP p m = m >>= guarded p
--  where
--    guarded p x = guard (p x) >> return x