{-# LANGUAGE GADTs, TypeFamilies, ScopedTypeVariables, OverloadedStrings, BangPatterns #-} -- | -- -- Primitives: -- -- * 'Event': 'mempty', 'mappend', 'fmap' -- -- * 'Reactive': 'fmap', 'return', 'join', '<' -- -- * 'scatterE' -- -- * 'accumE' or 'accumR' -- -- * 'stepper', 'apply' (or 'sample', or 'snapshotWith') -- -- * 'readChanE', 'writeChanE', 'getE', 'pollE', 'putE', 'runLoopUntil' -- module Control.Reactive ( -- * Types Event, Reactive, -- * Basic combinators -- ** Event to reactive stepper, -- switcher, maybeStepper, -- maybeSwitcher, -- sampleAndHold, sampleAndHold2, -- ** Reactive to event apply, filter', gate, sample, snapshot, snapshotWith, -- * Merging and splitting values justE, splitE, eitherE, -- filterE, -- retainE, -- partitionE, -- zipR, -- unzipR, -- * Past-dependent values -- ** Buffering events lastE, delayE, -- recallE, recallEWith, diffE, bufferE, gatherE, scatterE, -- ** Accumulating values accumE, accumR, foldpE, foldpR, scanlE, scanlR, mapAccum, -- ** Special accumulators firstE, restE, countE, countR, monoidE, monoidR, -- ** Lifted monoids sumE, productE, allE, anyE, sumR, productR, allR, anyR, -- * Toggles and switches tickE, onR, offR, toggleR, -- * Time -- Time, pulse, time, integral, -- * Record and playback TransportControl(..), transport, record, playback, playback', -- * Special functions seqE, oftenE, -- * Creating events and reactives -- ** From standard library getCharE, putCharE, getLineE, putLineE, systemTimeR, systemTimeSecondsR, systemTimeDayR, -- ** From channels readChanE, writeChanE, -- ** From IO getE, pollE, putE, -- modifyE, -- * Run events run, runLoop, runLoopUntil, -- * Utility Source, Sink, newSource, newSink, notify, showing, runEvent, runReactive, unsafeGetReactive, ) where import Prelude hiding (mapM) import Data.Time import Data.Monoid import Data.Maybe import Data.Either import Data.String import Data.VectorSpace hiding (Sum, getSum) import Control.Monad import Control.Applicative -- import Control.Newtype import Control.Concurrent (forkIO, forkOS, threadDelay) import System.IO.Unsafe import Control.Reactive.Chan import Control.Reactive.Var ------------------------------------------------------------------------------------- -- Primitives ------------------------------------------------------------------------------------- -- | -- A stream of values. -- -- > type Event a = [(Time, a)] -- data Event a where ENever :: Event a EMerge :: Event a -> Event a -> Event a ESeq :: Event a -> Event b -> Event b EMap :: (a -> b) -> Event a -> Event b EPred :: (a -> Bool) -> Event a -> Event a EConcat :: Event [a] -> Event a EChan :: Chan a -> Event a ESource :: IO [a] -> Event a ESink :: (a -> IO b) -> Event a -> Event b ESamp :: Reactive a -> Event b -> Event a -- | -- A time-varying value. -- -- > type Reactive a = Time -> a -- data Reactive a where RConst :: a -> Reactive a RStep :: Var a -> Event a -> Reactive a RAccum :: Var a -> Event (a -> a) -> Reactive a RApply :: Reactive (a -> b) -> Reactive a -> Reactive b -- RJoin :: Reactive (Reactive a) -> Reactive a prepE :: Event a -> IO (Event a) prepE (EMerge a b) = do a' <- prepE a b' <- prepE b return $ EMerge a' b' prepE (ESeq a b) = do a' <- prepE a b' <- prepE b return $ ESeq a' b' prepE (EMap f x) = do x' <- prepE x return $ EMap f x' prepE (EPred p x) = do x' <- prepE x return $ EPred p x' prepE (EConcat x) = do x' <- prepE x return $ EConcat x' prepE (ESink k a) = do a' <- prepE a return $ ESink k a' prepE (ESamp r x) = do r' <- prepR r x' <- prepE x return $ ESamp r' x' prepE (EChan ch) = do ch' <- prepC ch return $ ESource ch' prepE x = return x prepR :: Reactive a -> IO (Reactive a) prepR (RConst v) = do return $ RConst v prepR (RStep v x) = do x' <- prepE x v' <- prepV v return $ RStep v' x' prepR (RAccum v x) = do x' <- prepE x v' <- prepV v return $ RAccum v' x' prepR (RApply f x) = do f' <- prepR f x' <- prepR x return $ RApply f' x' -- prepR (RJoin r) = do -- r' <- prepR r -- return $ RJoin r' -- r'' <- prepR r' -- return $ RJoin r'' -- prepR x = return x prepC :: Chan a -> IO (IO [a]) prepC ch = do ch' <- dupChan ch return $ fmap maybeToList $ tryReadChan ch' prepV :: Var a -> IO (Var a) prepV v = dupVar v runE :: Event a -> IO [a] runE ENever = return [] runE (EMap f x) = fmap (fmap f) (runE x) runE (EPred p x) = fmap (filter p) (runE x) runE (EConcat x) = fmap concat (runE x) runE (EMerge a b) = liftM2 (++) (runE a) (runE b) runE (ESource i) = i runE (ESink o x) = runE x >>= mapM o runE (ESeq a b) = runE a >> runE b runE (ESamp r x) = do r' <- runRS r x' <- runE x return $ fmap (const r') x' -- case x' of -- [] -> return [] -- _ -> return [r'] runRS :: Reactive a -> IO a runRS = fmap last . runR -- Note: last is safe as reactives (per definition) always have at least one value runR :: Reactive a -> IO [a] runR (RConst v) = return [v] runR (RStep v x) = do v' <- readVar v x' <- runE x let !ys = (v':x') writeVar v (last ys) -- putStrLn $ "RStep, size is " ++ show (length x') return ys runR (RAccum v x) = do v' <- readVar v x' <- runE x let !w = (foldr (.) id x') v' writeVar v w -- putStrLn $ "RAccum, size is " ++ show (length x') return [w] runR (RApply f x) = do f' <- runR f x' <- runR x return $ f' <*> x' -- FIXME leaks here? -- FIXME we need an extra prepare here is the subnetwork is switched in -- runR (RJoin r) = do -- r' <- runRS r -- r_ <- prepR r' -- runR r_ -- r' <- runR r -- r_ <- mapM prepR r' -- r_' <- mapM runR r_ -- return $ concat r_' {-# INLINE runR #-} {-# INLINE runE #-} {-# INLINE runRS #-} ------------------------------------------------------------------------------------- -- Event API ------------------------------------------------------------------------------------- -- | -- Event is a functor: 'fmap' transforms each value. -- instance Functor (Event) where fmap = EMap -- | -- Event is a monoid: 'mempty' is the event that never occurs, 'mappend' interleaves values. -- instance Monoid (Event a) where mempty = ENever mappend = EMerge -- | -- The empty event. -- never :: Event a never = mempty -- | -- Interleave values. -- mergeE :: Event a -> Event a -> Event a mergeE = mappend -- | -- Interleave values of different types. -- eitherE :: Event a -> Event b -> Event (Either a b) a `eitherE` b = fmap Left a `mergeE` fmap Right b -- | -- Run both and behave as the second event. -- seqE :: Event a -> Event b -> Event b seqE = ESeq oftenE :: Event () oftenE = pollE $ return $ Just () -- | -- Map over values (synonym for @f \<$> xs@). mapE :: (a -> b) -> Event a -> Event b mapE = (<$>) -- | -- Filter values, semantically @filter p xs@. -- filterE :: (a -> Bool) -> Event a -> Event a filterE p = EPred p -- | -- Retain values, semantically @retain p xs@. -- retainE :: (a -> Bool) -> Event a -> Event a retainE p = EPred (not . p) -- | -- Separate chunks of values. -- -- > scatterE [e1,e2..] = [e1] <> [e2] .. -- scatterE :: Event [a] -> Event a scatterE = EConcat -- | -- Discard empty values. -- justE :: Event (Maybe a) -> Event a justE = EConcat . fmap maybeToList -- | -- Partition values, semantically @partition p xs@. -- -- > let (x, y) = partitionE p e in mergeE x y ≡ e -- partitionE :: (a -> Bool) -> Event a -> (Event a, Event a) partitionE p e = (filterE p e, retainE p e) -- | -- Partition values of different types. See also 'partitionE'. -- -- > let (x, y) in eitherE x y = splitE e ≡ e -- splitE :: Event (Either a b) -> (Event a, Event b) splitE e = (justE $ fromLeft <$> e, justE $ fromRight <$> e) unzipE :: Event (a, b) -> (Event a, Event b) unzipE e = (fst <$> e, snd <$> e) unzipR :: Reactive (a, b) -> (Reactive a, Reactive b) unzipR r = (fst <$> r, snd <$> r) -- | -- Replace values, semantically @x <$ e@. -- replaceE :: b -> Event a -> Event b replaceE x = (x <$) -- | -- Throw away values of the event. -- -- This is of course just @() <$ x@ but it is useful to fix the type in some cases. -- tickE :: Event a -> Event () tickE = replaceE () -- | -- Discard values, using an arbitrary empty element. -- tickME :: Monoid b => Event a -> Event b tickME = replaceE mempty -- | -- Event accumulator. -- -- > a `accumE` e = (a `accumR` e) `sample` e -- > a `accumR` e = a `stepper` (a `accumE` e) -- accumE :: a -> Event (a -> a) -> Event a a `accumE` e = (a `accumR` e) `sample` e -- | -- Create a past-dependent event. -- -- > scanlE f z x = foldpE (flip f) f z x -- foldpE :: (a -> b -> b) -> b -> Event a -> Event b foldpE f a e = a `accumE` (f <$> e) -- | -- Create a past-dependent event. This combinator corresponds to 'scanl' on streams. -- -- > scanlE f z x = foldpE (flip f) f z x -- scanlE :: (a -> b -> a) -> a -> Event b -> Event a scanlE f = foldpE (flip f) -- | -- Create a past-dependent event using a 'Monoid' instance. -- monoidE :: Monoid a => Event a -> Event a monoidE = scanlE mappend mempty liftMonoidE :: Monoid m => (a -> m) -> (m -> a) -> Event a -> Event a liftMonoidE i o = fmap o . monoidE . fmap i sumE :: Num a => Event a -> Event a sumE = liftMonoidE Sum getSum productE :: Num a => Event a -> Event a productE = liftMonoidE Product getProduct allE :: Event Bool -> Event Bool allE = liftMonoidE All getAll anyE :: Event Bool -> Event Bool anyE = liftMonoidE Any getAny -- | -- Get just the first value. -- firstE :: Event a -> Event a firstE = justE . fmap snd . foldpE g (True,Nothing) where g c (True, _) = (False,Just c) -- first time output g c (False, _) = (False,Nothing) -- then no output -- | -- Get all but the first value. -- restE :: Event a -> Event a restE = justE . fmap snd . foldpE g (True,Nothing) where g c (True, _) = (False,Nothing) -- first time no output g c (False, _) = (False,Just c) -- then output -- | -- Count values. -- countE :: Enum b => Event a -> Event b countE = accumE (toEnum 0) . fmap (const succ) -- | -- Delay by one value. -- lastE :: Event a -> Event a lastE = fmap snd . recallE -- | -- Delay by @n@ values. -- delayE :: Int -> Event a -> Event a delayE n = foldr (.) id (replicate n lastE) -- | -- Buffer up to /n/ values. When the buffer is full, old elements will be rotated out. -- -- > bufferE n e = [[e1],[e1,e2]..[e1..en],[e2..en+1]..] -- bufferE :: Int -> Event a -> Event [a] bufferE n = (reverse <$>) . foldpE g [] where g x xs = x : take (n-1) xs -- | -- Gather event values into chunks of regular size. -- -- > gatherE n e = [[e1..en],[en+1..e2n]..] -- gatherE :: Int -> Event a -> Event [a] gatherE n = (reverse <$>) . filterE (\xs -> length xs == n) . foldpE g [] where g x xs | length xs < n = x : xs | length xs == n = x : [] | otherwise = error "gatherE: Wrong length" -- | -- Pack with last value. -- recallE :: Event a -> Event (a, a) recallE = recallEWith (,) -- | -- Pack with last value. Similar to @withPrevEWith@ in reactive but flipped. -- -- recallEWith :: (a -> a -> b) -> Event a -> Event b -- recallEWith f = justE . fmap k . bufferE 2 -- where -- k [] = Nothing -- k [x] = Nothing -- k (a:b:_) = Just $ f a b recallEWith f e = (joinMaybes' . fmap combineMaybes) $ dup Nothing `accumE` fmap (shift . Just) e where shift b (_,a) = (a,b) dup x = (x,x) joinMaybes' = justE combineMaybes = uncurry (liftA2 f) {- TODO not sure about these eventMain :: Event (Maybe Bool) -> IO () eventMain = eventMain' . (fmap . fmap) (\r -> if r then ExitSuccess else ExitFailure (-1)) eventMain' :: Event (Maybe ExitCode) -> IO () eventMain' e = do code <- runLoopUntil e exitWith code -} ------------------------------------------------------------------------------------- -- Reactive API ------------------------------------------------------------------------------------- -- | -- Reactive has a lifted is a monoid: 'mempty' is the constant empty value and -- mappend combines values according to 'mappend' on values. -- instance Monoid a => Monoid (Reactive a) where mempty = pure mempty mappend = liftA2 mappend -- | -- Reactive is a functor: 'fmap' transforms the value at each point in time. -- instance Functor Reactive where fmap f = (pure f <*>) -- | -- Reactive is an applicative functor: 'pure' is a constant value and @fr \<*> xr@ applies the -- function @fr t@ to the value @xr t@. -- instance Applicative Reactive where pure = RConst -- pure x = x `stepper` never (<*>) = RApply -- instance Monad Reactive where -- return = pure -- x >>= k = (RJoin . fmap k) x instance IsString a => IsString (Reactive a) where fromString = pure . fromString instance Eq (Reactive b) where (==) = noFun "(==)" (/=) = noFun "(/=)" instance Ord b => Ord (Reactive b) where min = liftA2 min max = liftA2 max instance Enum a => Enum (Reactive a) where succ = fmap succ pred = fmap pred toEnum = pure . toEnum fromEnum = noFun "fromEnum" enumFrom = noFun "enumFrom" enumFromThen = noFun "enumFromThen" enumFromTo = noFun "enumFromTo" enumFromThenTo = noFun "enumFromThenTo" instance Num a => Num (Reactive a) where (+) = liftA2 (+) (*) = liftA2 (*) (-) = liftA2 (-) abs = fmap abs signum = fmap signum fromInteger = pure . fromInteger instance (Num a, Ord a) => Real (Reactive a) where toRational = noFun "toRational" instance Integral a => Integral (Reactive a) where quot = liftA2 quot rem = liftA2 rem div = liftA2 div mod = liftA2 mod quotRem = (fmap.fmap) unzipR (liftA2 quotRem) divMod = (fmap.fmap) unzipR (liftA2 divMod) toInteger = noFun "toInteger" instance Fractional b => Fractional (Reactive b) where recip = fmap recip fromRational = pure . fromRational instance Floating b => Floating (Reactive b) where pi = pure pi sqrt = fmap sqrt exp = fmap exp log = fmap log sin = fmap sin cos = fmap cos asin = fmap asin atan = fmap atan acos = fmap acos sinh = fmap sinh cosh = fmap cosh asinh = fmap asinh atanh = fmap atanh acosh = fmap acosh instance AdditiveGroup v => AdditiveGroup (Reactive v) where zeroV = pure zeroV (^+^) = liftA2 (^+^) negateV = liftA negateV instance VectorSpace v => VectorSpace (Reactive v) where type Scalar (Reactive v) = Scalar v (*^) s = fmap (s *^) -- | -- A non-reactive reactive. -- alwaysR :: a -> Reactive a alwaysR = pure -- | -- Step between values. -- stepper :: a -> Event a -> Reactive a stepper x e = RStep (newVar x) e -- | -- Switch between time-varying values. -- -- switcher :: Reactive a -> Event (Reactive a) -> Reactive a -- r `switcher` e = RJoin (r `stepper` e) -- r `switcher` e = join (r `stepper` e) -- | -- Step between values without initial. -- maybeStepper :: Event a -> Reactive (Maybe a) maybeStepper e = Nothing `stepper` fmap Just e -- | -- Switch between time-varying values without initial. -- -- maybeSwitcher :: Event (Reactive a) -> Reactive (Maybe a) -- maybeSwitcher e = pure Nothing `switcher` fmap (fmap Just) e -- | -- Step between values without initial, failing if sampled before the first step. -- eventToReactive :: Event a -> Reactive a eventToReactive = stepper (error "eventToReactive: ") -- | -- Switch between the values of a time-varying value when an event occurs. -- -- sampleAndHold :: Reactive b -> Event a -> Reactive b -- sampleAndHold r e = r `switcher` (pure <$> r `sample` e) -- sampleAndHold r e = (liftA2 change) r (maybeStepper $ sample r e) -- where -- change a Nothing = a -- change a (Just b) = b sampleAndHold2 :: b -> Reactive b -> Event a -> Reactive b sampleAndHold2 z r e = z `stepper` (r `sample` e) -- | -- Apply the values of an event to a time-varying function. -- -- > r `apply` e = r `snapshotWith ($)` e -- apply :: Reactive (a -> b) -> Event a -> Event b r `apply` e = r `o` e where o = snapshotWith ($) -- | -- Sample a time-varying value. -- -- > r `snapshot` e = snapshotWith const -- sample :: Reactive b -> Event a -> Event b sample = ESamp -- | -- Sample a time-varying value with the value of the trigger. -- -- > r `snapshot` e = snapshotWith (,) -- snapshot :: Reactive a -> Event b -> Event (a, b) snapshot = snapshotWith (,) -- | -- Sample a time-varying value with the value of the trigger, using the given -- function to combine. -- -- > r `snapshotWith f` e = (f <$> r) `apply` e -- snapshotWith :: (a -> b -> c) -> Reactive a -> Event b -> Event c snapshotWith f r e = sample (liftA2 f r (eventToReactive e)) e -- | -- Filter an event based on a time-varying predicate. -- -- > r `filter'` e = justE $ (partial <$> r) `apply` e -- filter' :: Reactive (a -> Bool) -> Event a -> Event a r `filter'` e = justE $ (partial <$> r) `apply` e -- | -- Filter an event based on a time-varying toggle. -- -- > r `gate` e = (const <$> r) `filter'` e -- gate :: Reactive Bool -> Event a -> Event a r `gate` e = (const <$> r) `filter'` e -- | -- Efficient combination of 'accumE' and 'accumR'. -- mapAccum :: a -> Event (a -> (b,a)) -> (Event b, Reactive a) mapAccum acc ef = (fst <$> e, stepper acc (snd <$> e)) where e = accumE (emptyAccum,acc) ((. snd) <$> ef) emptyAccum = error "mapAccum: Empty accumulator" -- | -- Combine reactives. See also 'eitherE'. -- zipR :: Reactive a -> Reactive b -> Reactive (a, b) zipR = liftA2 (,) -- | -- Reactive accumulator. -- -- > a `accumE` e = (a `accumR` e) `sample` e -- > a `accumR` e = a `stepper` (a `accumE` e) -- accumR :: a -> Event (a -> a) -> Reactive a accumR x = RAccum (newVar x) -- | -- Create a past-dependent reactive. This combinator corresponds to 'scanl' on streams. -- -- > scanlR f z x = foldpR (flip f) f z x -- foldpR :: (a -> b -> b) -> b -> Event a -> Reactive b foldpR f = scanlR (flip f) -- | -- Create a past-dependent reactive. This combinator corresponds to 'scanl' on streams. -- -- > scanlR f z x = foldpR (flip f) f z x -- scanlR :: (a -> b -> a) -> a -> Event b -> Reactive a scanlR f a e = a `stepper` scanlE f a e -- | -- Create a past-dependent event using a 'Monoid' instance. -- monoidR :: Monoid a => Event a -> Reactive a monoidR = scanlR mappend mempty liftMonoidR :: Monoid m => (a -> m) -> (m -> a) -> Event a -> Reactive a liftMonoidR i o = fmap o . monoidR . fmap i sumR :: Num a => Event a -> Reactive a sumR = liftMonoidR Sum getSum productR :: Num a => Event a -> Reactive a productR = liftMonoidR Product getProduct allR :: Event Bool -> Reactive Bool allR = liftMonoidR All getAll anyR :: Event Bool -> Reactive Bool anyR = liftMonoidR Any getAny -- | -- Count values. -- countR :: Enum b => Event a -> Reactive b countR = accumR (toEnum 0) . fmap (const succ) onR :: Event a -> Reactive Bool onR = fmap isJust . maybeStepper offR :: Event a -> Reactive Bool offR = fmap not . onR toggleR :: Event a -> Reactive Bool toggleR = fmap odd . countR -- | -- Difference of successive values. -- diffE :: Num a => Event a -> Event a diffE = recallEWith $ flip (-) -- | -- A generalized time behaviour. -- time :: Fractional a => Reactive a time = accumR 0 ((+ kStdPulseInterval) <$ kStdPulse) -- | -- Integrates a behaviour. -- -- > integral pulse behavior -- integral :: Fractional b => Event a -> Reactive b -> Reactive b integral t b = sumR (snapshotWith (*) b (diffE (tx `sample` t))) where -- tx = time tx :: Fractional a => Reactive a tx = fmap (fromRational . toRational) $ systemTimeSecondsR data TransportControl t = Play -- ^ Play from the current position. | Reverse -- ^ Play in reverse from the current position. | Pause -- ^ Stop playing, and retain current position. | Stop -- ^ Stop and reset position. deriving (Eq, Ord, Show) -- | Seek t -- ^ Set current position. isStop Stop = True isStop _ = False -- | -- Generates a cursor that moves forward or backward continuously. -- -- The cursor may be started, stopped, moved by sending a 'TransportControl' event. -- -- > transport control pulse speed -- transport :: (Ord t, Fractional t) => Event (TransportControl t) -> Event a -> Reactive t -> Reactive t transport ctrl trig speed = position' where -- action :: Reactive (TransportControl t) action = Pause `stepper` ctrl -- direction :: Num a => Reactive a direction = action <$$> \a -> case a of Play -> 1 Reverse -> (-1) Pause -> 0 Stop -> 0 -- position :: Num a => Reactive a position = integral trig (speed * direction) -- startPosition = position `sampleAndHold` (filterE isStop ctrl) startPosition = sampleAndHold2 0 position (filterE isStop ctrl) position' = position - startPosition -- | -- Record a list of values. -- record :: Ord t => Reactive t -> Event a -> Reactive [(t, a)] record t x = foldpR append [] (t `snapshot` x) where append x xs = xs ++ [x] -- | -- Play back a list of values. -- -- This function will sample the time behaviour at an arbitrary -- small interval. To get precise control of how time is sampled, -- use 'playback'' instead. -- playback :: Ord t => Reactive t -> Reactive [(t,a)] -> Event a playback t s = scatterE $ fmap snd <$> playback' oftenE t s -- | -- Play back a list of values. -- playback' :: Ord t => Event b -> Reactive t -> Reactive [(t,a)] -> Event [(t, a)] playback' p t s = cursor s (t `sample` p) where -- cursor :: Ord t => Reactive [(t,a)] -> Event t -> Event [(a,t)] cursor s = snapshotWith (flip occs) s . recallE -- occs :: Ord t => (t,t) -> [(a,t)] -> [(a,t)] occs (x,y) = filter (\(t,_) -> x < t && t <= y) {- modify :: Event (a -> a) -> Reactive a -> Reactive a set :: Event a -> Reactive b -> Reactive a -} ------------------------------------------------------------------------------------- -- Lifting IO etc ------------------------------------------------------------------------------------- -- | -- Event reading from external world. -- The computation should be blocking and is polled exactly once per value. -- -- This function can be used with standard I/O functions. -- getE :: IO a -> Event a getE k = unsafePerformIO $ do ch <- newChan forkIO $ cycleM $ k >>= writeChan ch return (EChan ch) -- | -- Event reading from external world. -- The computation should be non-blocking and may be polled repeatedly for each value. -- -- This function should be used with /non-effectful/ functions, typically functions that -- observe the current value of some external property. -- You should /not/ use this function with standard I/O functions as this -- may lead to non-deterministic reads (i.e. loss of data). -- pollE :: IO (Maybe a) -> Event a pollE = ESource . fmap maybeToList -- Event interacting with the external world. -- The computation should be non-blocking and its values will be contested. -- -- modifyE :: (a -> IO b) -> Event a -> Event b -- modifyE = ESink -- | -- Event writing to the external world. -- -- This function can be used with standard I/O functions. -- putE :: (a -> IO ()) -> Event a -> Event a putE k = ESink $ \x -> do k x return x -- | -- Event reading from a channel. -- readChanE :: Chan a -> Event a readChanE = EChan -- | -- Event writing to a channel. -- writeChanE :: Chan a -> Event a -> Event a writeChanE ch e = ESink (writeChan ch) e `seqE` e -- | -- Event version of 'getChar'. -- getCharE :: Event Char getCharE = getE getChar -- | -- Event version of 'putChar'. -- putCharE :: Event Char -> Event Char putCharE = putE putChar -- | -- Event version of 'getLine'. -- getLineE :: Event String getLineE = getE getLine -- | -- Event version of 'putStrLn'. -- putLineE :: Event String -> Event String putLineE = putE putStrLn systemTimeR :: Reactive UTCTime systemTimeR = eventToReactive $ pollE (Just <$> getCurrentTime) systemTimeSecondsR :: Reactive DiffTime systemTimeSecondsR = fmap utctDayTime systemTimeR systemTimeDayR :: Reactive Day systemTimeDayR = fmap utctDay systemTimeR -- |  -- An event occuring at the specified interval. -- pulse :: DiffTime -> Event () pulse t = getE $ threadDelay (round (fromMicro t)) where fromMicro = (* 1000000) ------------------------------------------------------------------------------------- -- Running ------------------------------------------------------------------------------------- -- | -- Run the given event once. -- run :: Event a -> IO () run e = do f <- prepE e runE f return () -- | -- Run the given event for ever. -- runLoop :: Event a -> IO () runLoop e = do f <- prepE e runLoop' f where runLoop' g = do runE g threadDelay kLoopInterval >> runLoop' g -- | -- Run the given event until the first @Just x@ value, then return @x@. -- runLoopUntil :: Event (Maybe a) -> IO a runLoopUntil e = do f <- prepE e runLoopUntil' f where runLoopUntil' g = do r <- runE g case (catMaybes r) of [] -> threadDelay kLoopInterval >> runLoopUntil' g (a:_) -> return a ------------------------------------------------------------------------------------- -- Utility ------------------------------------------------------------------------------------- type Source a = Event a type Sink a = Event a -> Event () -- | -- Behaves like the original event but writes a given message to the standard -- output for each value. -- notify :: String -> Event a -> Event a notify m x = putLineE (fmap (const m) x) `seqE` x -- | -- Behaves like the original event but writes its value, prepended by the given -- message, for each value. -- showing :: Show a => String -> Event a -> Event a showing m x = putE k x where k x = putStrLn $ m ++ show x -- | -- Creates a new source and a computation that writes it. -- newSource :: IO (a -> IO (), Source a) newSource = do ch <- newChan return (writeChan ch, readChanE ch) -- | -- Creates a new sink and a computation that reads from it. -- newSink :: IO (IO (Maybe a), Sink a) newSink = do ch <- newChan return (tryReadChan ch, tickE . writeChanE ch) runEvent :: Show a => Event a -> IO () runEvent = runLoop . showing "" runReactive :: Show a => Reactive a -> IO () runReactive r = runEvent (r `sample` pulse (1/20)) unsafeGetReactive :: Reactive a -> a unsafeGetReactive r = unsafePerformIO $ runRS r ------------------------------------------------------------------------------------- partial :: (a -> Bool) -> (a -> Maybe a) partial p x | p x = Just x | otherwise = Nothing list z f [] = z list z f xs = f xs filterMap p = catMaybes . map p cycleM x = x >> cycleM x single x = [x] -- | 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 fromLeft (Left a) = Just a fromLeft (Right b) = Nothing fromRight (Left a) = Nothing fromRight (Right b) = Just b noFun = noOverloading "Reactive" noOverloading ty meth = error $ meth ++ ": No overloading for " ++ ty kStdPulseInterval :: Fractional a => a kStdPulseInterval = (1/20) kLoopInterval = round $ (1/20) * 1000000 -- us -- FIXME strange if lookInterval and stdPulse differ kStdPulse = pulse kStdPulseInterval (<$$>) = flip fmap