{----------------------------------------------------------------------------- Reactive Banana ------------------------------------------------------------------------------} {-# LANGUAGE Rank2Types, MultiParamTypeClasses, TypeSynonymInstances, FlexibleInstances #-} module Reactive.Banana.Experimental.Calm ( -- * Synopsis -- | Experimental module: API change very likely. -- -- 'Event' type that disallows simultaneous event occurrences. -- -- The combinators behave essentially as their counterparts -- in "Reactive.Banana.Combinators". -- * Main types Event, Behavior, collect, fromCalm, interpret, -- * Core Combinators module Control.Applicative, never, unionWith, filterE, accumE, apply, stepper, -- * Derived Combinators -- ** Filtering filterJust, -- ** Accumulation -- $Accumulation. accumB, mapAccum, -- ** Apply class (<@>), (<@), ) where import Control.Applicative import Control.Monad import Data.Maybe (listToMaybe) import qualified Reactive.Banana.Combinators as Prim import qualified Reactive.Banana.Combinators {----------------------------------------------------------------------------- Main types ------------------------------------------------------------------------------} newtype Event t a = E { unE :: Prim.Event t a } type Behavior t = Reactive.Banana.Combinators.Behavior t -- | Convert event with possible simultaneous occurrences -- into an 'Event' with a single occurrence. collect :: Reactive.Banana.Combinators.Event t a -> Event t [a] collect = E . Prim.collect -- | Convert event with single occurrences into -- event with possible simultaneous occurrences fromCalm :: Event t a -> Reactive.Banana.Combinators.Event t a fromCalm = unE singleton x = [x] -- | Interpretation function. -- Useful for testing. interpret :: (forall t. Event t a -> Event t b) -> [a] -> IO [Maybe b] interpret f xs = map listToMaybe <$> Prim.interpret (unE . f . E) (map singleton xs) {----------------------------------------------------------------------------- Core Combinators ------------------------------------------------------------------------------} -- | Event that never occurs. -- Think of it as @never = []@. never :: Event t a never = E $ Prim.never -- | Merge two event streams of the same type. -- Combine simultaneous values if necessary. unionWith :: (a -> a -> a) -> Event t a -> Event t a -> Event t a unionWith f e1 e2 = E $ Prim.unionWith f (unE e1) (unE e2) -- | Allow all events that fulfill the predicate, discard the rest. filterE :: (a -> Bool) -> Event t a -> Event t a filterE p = E . Prim.filterE p . unE -- | Construct a time-varying function from an initial value and -- a stream of new values. stepper :: a -> Event t a -> Behavior t a stepper x e = Prim.stepper x (unE e) -- | The 'accumE' function accumulates a stream of events. accumE :: a -> Event t (a -> a) -> Event t a accumE acc = E . Prim.accumE acc . unE -- | Apply a time-varying function to a stream of events. apply :: Behavior t (a -> b) -> Event t a -> Event t b apply b = E . Prim.apply b . unE instance Functor (Event t) where fmap f = E . fmap f . unE {----------------------------------------------------------------------------- Derived Combinators ------------------------------------------------------------------------------} -- | Keep only the 'Just' values. -- Variant of 'filterE'. filterJust :: Event t (Maybe a) -> Event t a filterJust = E . Prim.filterJust . unE -- | The 'accumB' function is similar to a /strict/ left fold, 'foldl''. -- It starts with an initial value and combines it with incoming events. accumB :: a -> Event t (a -> a) -> Behavior t a accumB acc = Prim.accumB acc . unE -- $Accumulation. -- Note: all accumulation functions are strict in the accumulated value! -- acc -> (x,acc) is the order used by 'unfoldr' and 'State'. -- | Efficient combination of 'accumE' and 'accumB'. mapAccum :: acc -> Event t (acc -> (x,acc)) -> (Event t x, Behavior t acc) mapAccum acc ef = let (e,b) = Prim.mapAccum acc (unE ef) in (E e, b) -- | Infix synonym for the 'apply' combinator. Similar to '<*>'. (<@>) :: Behavior t (a -> b) -> Event t a -> Event t b (<@>) = apply -- | Tag all event occurrences with a time-varying value. Similar to '<*'. (<@) :: Behavior t a -> Event t b -> Event t a f <@ g = (const <$> f) <@> g