packages feed

elerea 2.0.0 → 2.1.0

raw patch · 7 files changed

+250/−128 lines, 7 filesPVP ok

version bump matches the API change (PVP)

API changes (from Hackage documentation)

- FRP.Elerea.Clocked: instance (Bounded t) => Bounded (Signal t)
- FRP.Elerea.Clocked: instance (Enum t) => Enum (Signal t)
- FRP.Elerea.Clocked: instance (Floating t) => Floating (Signal t)
- FRP.Elerea.Clocked: instance (Fractional t) => Fractional (Signal t)
- FRP.Elerea.Clocked: instance (Integral t) => Integral (Signal t)
- FRP.Elerea.Clocked: instance (Num t) => Num (Signal t)
- FRP.Elerea.Clocked: instance (Ord t) => Ord (Signal t)
- FRP.Elerea.Clocked: instance (Real t) => Real (Signal t)
- FRP.Elerea.Delayed: instance (Bounded t) => Bounded (Signal p t)
- FRP.Elerea.Delayed: instance (Enum t) => Enum (Signal p t)
- FRP.Elerea.Delayed: instance (Floating t) => Floating (Signal p t)
- FRP.Elerea.Delayed: instance (Fractional t) => Fractional (Signal p t)
- FRP.Elerea.Delayed: instance (Integral t) => Integral (Signal p t)
- FRP.Elerea.Delayed: instance (Num t) => Num (Signal p t)
- FRP.Elerea.Delayed: instance (Ord t) => Ord (Signal p t)
- FRP.Elerea.Delayed: instance (Real t) => Real (Signal p t)
- FRP.Elerea.Legacy.Internal: instance (Bounded t) => Bounded (Signal t)
- FRP.Elerea.Legacy.Internal: instance (Enum t) => Enum (Signal t)
- FRP.Elerea.Legacy.Internal: instance (Floating t) => Floating (Signal t)
- FRP.Elerea.Legacy.Internal: instance (Fractional t) => Fractional (Signal t)
- FRP.Elerea.Legacy.Internal: instance (Integral t) => Integral (Signal t)
- FRP.Elerea.Legacy.Internal: instance (Num t) => Num (Signal t)
- FRP.Elerea.Legacy.Internal: instance (Ord t) => Ord (Signal t)
- FRP.Elerea.Legacy.Internal: instance (Real t) => Real (Signal t)
- FRP.Elerea.Param: instance (Bounded t) => Bounded (Signal p t)
- FRP.Elerea.Param: instance (Enum t) => Enum (Signal p t)
- FRP.Elerea.Param: instance (Floating t) => Floating (Signal p t)
- FRP.Elerea.Param: instance (Fractional t) => Fractional (Signal p t)
- FRP.Elerea.Param: instance (Integral t) => Integral (Signal p t)
- FRP.Elerea.Param: instance (Num t) => Num (Signal p t)
- FRP.Elerea.Param: instance (Ord t) => Ord (Signal p t)
- FRP.Elerea.Param: instance (Real t) => Real (Signal p t)
- FRP.Elerea.Param: instance Applicative (Signal p)
- FRP.Elerea.Param: instance Eq (Signal p a)
- FRP.Elerea.Param: instance Functor (Signal p)
- FRP.Elerea.Param: instance Monad (Signal p)
- FRP.Elerea.Param: instance Show (Signal p a)
- FRP.Elerea.Simple: instance (Bounded t) => Bounded (Signal t)
- FRP.Elerea.Simple: instance (Enum t) => Enum (Signal t)
- FRP.Elerea.Simple: instance (Floating t) => Floating (Signal t)
- FRP.Elerea.Simple: instance (Fractional t) => Fractional (Signal t)
- FRP.Elerea.Simple: instance (Integral t) => Integral (Signal t)
- FRP.Elerea.Simple: instance (Num t) => Num (Signal t)
- FRP.Elerea.Simple: instance (Ord t) => Ord (Signal t)
- FRP.Elerea.Simple: instance (Real t) => Real (Signal t)
+ FRP.Elerea.Clocked: debug :: String -> SignalGen ()
+ FRP.Elerea.Clocked: instance Bounded t => Bounded (Signal t)
+ FRP.Elerea.Clocked: instance Enum t => Enum (Signal t)
+ FRP.Elerea.Clocked: instance Floating t => Floating (Signal t)
+ FRP.Elerea.Clocked: instance Fractional t => Fractional (Signal t)
+ FRP.Elerea.Clocked: instance Integral t => Integral (Signal t)
+ FRP.Elerea.Clocked: instance Num t => Num (Signal t)
+ FRP.Elerea.Clocked: instance Ord t => Ord (Signal t)
+ FRP.Elerea.Clocked: instance Real t => Real (Signal t)
+ FRP.Elerea.Clocked: until :: Signal Bool -> SignalGen (Signal Bool)
+ FRP.Elerea.Delayed: instance Bounded t => Bounded (Signal p t)
+ FRP.Elerea.Delayed: instance Enum t => Enum (Signal p t)
+ FRP.Elerea.Delayed: instance Floating t => Floating (Signal p t)
+ FRP.Elerea.Delayed: instance Fractional t => Fractional (Signal p t)
+ FRP.Elerea.Delayed: instance Integral t => Integral (Signal p t)
+ FRP.Elerea.Delayed: instance Num t => Num (Signal p t)
+ FRP.Elerea.Delayed: instance Ord t => Ord (Signal p t)
+ FRP.Elerea.Delayed: instance Real t => Real (Signal p t)
+ FRP.Elerea.Legacy.Internal: instance Bounded t => Bounded (Signal t)
+ FRP.Elerea.Legacy.Internal: instance Enum t => Enum (Signal t)
+ FRP.Elerea.Legacy.Internal: instance Floating t => Floating (Signal t)
+ FRP.Elerea.Legacy.Internal: instance Fractional t => Fractional (Signal t)
+ FRP.Elerea.Legacy.Internal: instance Integral t => Integral (Signal t)
+ FRP.Elerea.Legacy.Internal: instance Num t => Num (Signal t)
+ FRP.Elerea.Legacy.Internal: instance Ord t => Ord (Signal t)
+ FRP.Elerea.Legacy.Internal: instance Real t => Real (Signal t)
+ FRP.Elerea.Param: embed :: Signal p' -> SignalGen p' a -> SignalGen p a
+ FRP.Elerea.Param: input :: SignalGen p (Signal p)
+ FRP.Elerea.Param: instance Applicative Signal
+ FRP.Elerea.Param: instance Bounded t => Bounded (Signal t)
+ FRP.Elerea.Param: instance Enum t => Enum (Signal t)
+ FRP.Elerea.Param: instance Eq (Signal a)
+ FRP.Elerea.Param: instance Floating t => Floating (Signal t)
+ FRP.Elerea.Param: instance Fractional t => Fractional (Signal t)
+ FRP.Elerea.Param: instance Functor Signal
+ FRP.Elerea.Param: instance Integral t => Integral (Signal t)
+ FRP.Elerea.Param: instance Monad Signal
+ FRP.Elerea.Param: instance Num t => Num (Signal t)
+ FRP.Elerea.Param: instance Ord t => Ord (Signal t)
+ FRP.Elerea.Param: instance Real t => Real (Signal t)
+ FRP.Elerea.Param: instance Show (Signal a)
+ FRP.Elerea.Param: until :: Signal Bool -> SignalGen p (Signal Bool)
+ FRP.Elerea.Simple: debug :: String -> SignalGen ()
+ FRP.Elerea.Simple: instance Bounded t => Bounded (Signal t)
+ FRP.Elerea.Simple: instance Enum t => Enum (Signal t)
+ FRP.Elerea.Simple: instance Floating t => Floating (Signal t)
+ FRP.Elerea.Simple: instance Fractional t => Fractional (Signal t)
+ FRP.Elerea.Simple: instance Integral t => Integral (Signal t)
+ FRP.Elerea.Simple: instance Num t => Num (Signal t)
+ FRP.Elerea.Simple: instance Ord t => Ord (Signal t)
+ FRP.Elerea.Simple: instance Real t => Real (Signal t)
+ FRP.Elerea.Simple: until :: Signal Bool -> SignalGen (Signal Bool)
- FRP.Elerea.Clocked: getRandom :: (MTRandom a) => SignalGen a
+ FRP.Elerea.Clocked: getRandom :: MTRandom a => SignalGen a
- FRP.Elerea.Clocked: noise :: (MTRandom a) => SignalGen (Signal a)
+ FRP.Elerea.Clocked: noise :: MTRandom a => SignalGen (Signal a)
- FRP.Elerea.Delayed: getRandom :: (MTRandom a) => SignalGen p a
+ FRP.Elerea.Delayed: getRandom :: MTRandom a => SignalGen p a
- FRP.Elerea.Delayed: noise :: (MTRandom a) => SignalGen p (Signal p a)
+ FRP.Elerea.Delayed: noise :: MTRandom a => SignalGen p (Signal p a)
- FRP.Elerea.Legacy: (/=@) :: (Eq a) => Signal a -> Signal a -> Signal Bool
+ FRP.Elerea.Legacy: (/=@) :: Eq a => Signal a -> Signal a -> Signal Bool
- FRP.Elerea.Legacy: (<=@) :: (Ord a) => Signal a -> Signal a -> Signal Bool
+ FRP.Elerea.Legacy: (<=@) :: Ord a => Signal a -> Signal a -> Signal Bool
- FRP.Elerea.Legacy: (<@) :: (Ord a) => Signal a -> Signal a -> Signal Bool
+ FRP.Elerea.Legacy: (<@) :: Ord a => Signal a -> Signal a -> Signal Bool
- FRP.Elerea.Legacy: (==@) :: (Eq a) => Signal a -> Signal a -> Signal Bool
+ FRP.Elerea.Legacy: (==@) :: Eq a => Signal a -> Signal a -> Signal Bool
- FRP.Elerea.Legacy: (>=@) :: (Ord a) => Signal a -> Signal a -> Signal Bool
+ FRP.Elerea.Legacy: (>=@) :: Ord a => Signal a -> Signal a -> Signal Bool
- FRP.Elerea.Legacy: (>@) :: (Ord a) => Signal a -> Signal a -> Signal Bool
+ FRP.Elerea.Legacy: (>@) :: Ord a => Signal a -> Signal a -> Signal Bool
- FRP.Elerea.Legacy: signalDebug :: (Show a) => a -> SignalMonad ()
+ FRP.Elerea.Legacy: signalDebug :: Show a => a -> SignalMonad ()
- FRP.Elerea.Legacy.Internal: signalDebug :: (Show a) => a -> SignalMonad ()
+ FRP.Elerea.Legacy.Internal: signalDebug :: Show a => a -> SignalMonad ()
- FRP.Elerea.Param: data Signal p a
+ FRP.Elerea.Param: data Signal a
- FRP.Elerea.Param: delay :: a -> Signal p a -> SignalGen p (Signal p a)
+ FRP.Elerea.Param: delay :: a -> Signal a -> SignalGen p (Signal a)
- FRP.Elerea.Param: external :: a -> IO (Signal p a, a -> IO ())
+ FRP.Elerea.Param: external :: a -> IO (Signal a, a -> IO ())
- FRP.Elerea.Param: externalMulti :: IO (SignalGen p (Signal p [a]), a -> IO ())
+ FRP.Elerea.Param: externalMulti :: IO (SignalGen p (Signal [a]), a -> IO ())
- FRP.Elerea.Param: generator :: Signal p (SignalGen p a) -> SignalGen p (Signal p a)
+ FRP.Elerea.Param: generator :: Signal (SignalGen p a) -> SignalGen p (Signal a)
- FRP.Elerea.Param: getRandom :: (MTRandom a) => SignalGen p a
+ FRP.Elerea.Param: getRandom :: MTRandom a => SignalGen p a
- FRP.Elerea.Param: memo :: Signal p a -> SignalGen p (Signal p a)
+ FRP.Elerea.Param: memo :: Signal a -> SignalGen p (Signal a)
- FRP.Elerea.Param: noise :: (MTRandom a) => SignalGen p (Signal p a)
+ FRP.Elerea.Param: noise :: MTRandom a => SignalGen p (Signal a)
- FRP.Elerea.Param: start :: SignalGen p (Signal p a) -> IO (p -> IO a)
+ FRP.Elerea.Param: start :: SignalGen p (Signal a) -> IO (p -> IO a)
- FRP.Elerea.Param: stateful :: a -> (p -> a -> a) -> SignalGen p (Signal p a)
+ FRP.Elerea.Param: stateful :: a -> (p -> a -> a) -> SignalGen p (Signal a)
- FRP.Elerea.Param: transfer :: a -> (p -> t -> a -> a) -> Signal p t -> SignalGen p (Signal p a)
+ FRP.Elerea.Param: transfer :: a -> (p -> t -> a -> a) -> Signal t -> SignalGen p (Signal a)
- FRP.Elerea.Simple: getRandom :: (MTRandom a) => SignalGen a
+ FRP.Elerea.Simple: getRandom :: MTRandom a => SignalGen a
- FRP.Elerea.Simple: noise :: (MTRandom a) => SignalGen (Signal a)
+ FRP.Elerea.Simple: noise :: MTRandom a => SignalGen (Signal a)

Files

CHANGES view
@@ -1,10 +1,23 @@+2.1.0 - 100805+* reimplemented the parametric variant in a way that doesn't require+  signals to carry the type of the parameter any more+* added the ability to extract the global input in the parametric+  variant, and also to override it (input and embed, resp.)+* added until to be able to define switchers that can truly drop+  references to old signals+* added debug printing capability to the simple and clocked variants+* made a note about possibly deprecating the delayed variant+ 2.0.0 - 100718-* moved experimental branch to the top (version 1 went into legacy status)-* added the clocked version+* moved experimental branch to the top (version 1 went into legacy+  status)+* added the clocked variant  1.2.3 - 100131-* added externalMulti to handle events that can fire several times within a superstep-* added a cache to the noise signal for safety reasons, so it lives in SignalGen now+* added externalMulti to handle events that can fire several times+  within a superstep+* added a cache to the noise signal for safety reasons, so it lives in+  SignalGen now  1.2.2 - 100115 * added noise signals and the getRandom primitive (using mersenne-random)@@ -13,7 +26,7 @@ * modified the &&@ and ||@ operators to short-circuit  1.2.0 - 091202-* added the delayed version to the experimental branch+* added the delayed variant to the experimental branch * renamed storeJust to (-->) in the experimental branch  1.1.0 - 091126@@ -42,8 +55,10 @@  0.2.0 - 090412 * removed primitives time and stateless-* removed default delay on stateful combinators and added experimental cycle detection-* added some non-primitive combinators: delay, edge, comparisons, logic relations+* removed default delay on stateful combinators and added experimental+  cycle detection+* added some non-primitive combinators: delay, edge, comparisons,+  logic relations * added signal instances for various numeric classes  0.1.0 - 090410
FRP/Elerea/Clocked.hs view
@@ -3,7 +3,7 @@ {-|  This version differs from the simple one in adding associated freeze-control signals ("clocks") to stateful entities to be able to pause+control signals (\'clocks\') to stateful entities to be able to pause entire subnetworks without having to write all the low-level logic explicitly.  The clocks are fixed to signals upon their creation, and the 'withClock' function can be used to specify the common clock for@@ -84,12 +84,14 @@     , externalMulti     , delay     , generator-    , withClock     , memo+    , until+    , withClock     , stateful     , transfer     , noise     , getRandom+    , debug     ) where  import Control.Applicative@@ -98,6 +100,7 @@ import Control.Monad.Fix import Data.IORef import Data.Maybe+import Prelude hiding (until) import System.Mem.Weak import System.Random.Mersenne @@ -252,6 +255,28 @@    addSignal (const sample) (const (sample >> return ())) ref pool +-- | A signal that is true exactly once: the first time the input+-- signal is true.  Afterwards, it is constantly false, and it holds+-- no reference to the input signal.  Note that 'until' always follows+-- the master clock, i.e. the fastest one, therefore it never creates+-- a long spike of @True@.+until :: Signal Bool             -- ^ the boolean input signal+      -> SignalGen (Signal Bool) -- ^ a one-shot signal true only the first time the input is true+until (S s) = SG $ \pool _ -> do+  ref <- newIORef (Ready undefined)++  rsmp <- mfix $ \rs -> newIORef $ do+    x <- s+    writeIORef ref (Updated undefined x)+    when x $ writeIORef rs $ do+      writeIORef ref (Updated undefined False)+      return False+    return x++  let sample = join (readIORef rsmp)++  addSignal (const sample) (const (() <$ sample)) ref pool+ -- | A signal that can be directly fed through the sink function -- returned.  This can be used to attach the network to the outer -- world.@@ -318,6 +343,10 @@ -- | A random source within the 'SignalGen' monad. getRandom :: MTRandom a => SignalGen a getRandom = SG (const (const randomIO))++-- | A printing action within the 'SignalGen' monad.+debug :: String -> SignalGen ()+debug = SG . const . const . putStrLn  -- The Show instance is only defined for the sake of Num... instance Show (Signal a) where
FRP/Elerea/Delayed.hs view
@@ -1,9 +1,15 @@ {-| -This version differs from the parametric one in introducing autmatic+Note: this module is likely to be deprecated in the near future,+because automatic delays are ill-defined, and not very useful in+practice anyway.  Experience with the library suggests that+instantaneous loops are relatively easy to avoid.++This version differs from the parametric one in introducing automatic delays.  In practice, if a dependency loop involves a 'transfer' primitive, it will be resolved during runtime even if transfer-functions are not delayed by default.+functions are not delayed by default.  Also, the until construct is+missing from this module.  The interface of this module differs from the old Elerea in the following ways:
FRP/Elerea/Legacy.hs view
@@ -27,13 +27,12 @@  integral x0 s = transfer x0 (\\dt x x0 -> x0+x*realToFrac dt) s @ -Head to "FRP.Elerea.Internal" for the implementation details.  To get-a general idea how to use the library, check out the sources in the-@elerea-examples@ package.+Head to "FRP.Elerea.Legacy.Internal" for the implementation details.+To get a general idea how to use the library, check out the sources in+the @elerea-examples@ package. -The "FRP.Elerea.Experimental" branch provides a similar interface with-a rather different underlying structure, which is likely to be more-efficient.+The "FRP.Elerea" branch provides a similar interface with a rather+different underlying structure, which is likely to be more efficient.  -} 
FRP/Elerea/Param.hs view
@@ -1,3 +1,5 @@+{-# LANGUAGE GeneralizedNewtypeDeriving #-}+ {-|  This version differs from the simple one in providing an extra@@ -26,8 +28,11 @@   (i.e. their behaviour doesn't depend on the context any more);  * the user needs to cache the results of applicative operations to be-  reused in multiple places explicitly using the 'memo' combinator.+  reused in multiple places explicitly using the 'memo' combinator; +* the input can be retrieved as an explicit signal within the+  SignalGen monad, and also overridden for parts of the network.+ -}  module FRP.Elerea.Param@@ -37,10 +42,13 @@     , external     , externalMulti     , delay+    , generator+    , memo+    , until+    , input+    , embed     , stateful     , transfer-    , memo-    , generator     , noise     , getRandom     , debug@@ -52,38 +60,28 @@ import Control.Monad.Fix import Data.IORef import Data.Maybe+import Prelude hiding (until) import System.Mem.Weak import System.Random.Mersenne  -- | A signal can be thought of as a function of type @Nat -> a@, and -- its 'Monad' instance agrees with that intuition.  Internally, is -- represented by a sampling computation.-newtype Signal p a = S { unS :: p -> IO a }+newtype Signal a = S (IO a) deriving (Functor, Applicative, Monad)  -- | A dynamic set of actions to update a network without breaking -- consistency.-type UpdatePool p = [Weak (p -> IO (), IO ())]+type UpdatePool = [Weak (IO (), IO ())]  -- | A signal generator is the only source of stateful signals. -- Internally, computes a signal structure and adds the new variables -- to an existing update pool.-newtype SignalGen p a = SG { unSG :: IORef (UpdatePool p) -> IO a }+newtype SignalGen p a = SG { unSG :: IORef UpdatePool -> Signal p -> IO a }  -- | The phases every signal goes through during a superstep: before -- or after sampling. data Phase s a = Ready s | Aged s a -instance Functor (Signal p) where-  fmap = liftM--instance Applicative (Signal p) where-  pure = return-  (<*>) = ap--instance Monad (Signal p) where-  return = S . const . return-  S g >>= f = S $ \p -> g p >>= \x -> unS (f x) p- instance Functor (SignalGen p) where   fmap = liftM @@ -92,11 +90,11 @@   (<*>) = ap  instance Monad (SignalGen p) where-  return = SG . const . return-  SG g >>= f = SG $ \p -> g p >>= \x -> unSG (f x) p+  return = SG . const . const . return+  SG g >>= f = SG $ \p i -> g p i >>= \x -> unSG (f x) p i  instance MonadFix (SignalGen p) where-  mfix f = SG $ \p -> mfix (($p).unSG.f)+  mfix f = SG $ \p i -> mfix (($i).($p).unSG.f)  -- | Embedding a signal into an 'IO' environment.  Repeated calls to -- the computation returned cause the whole network to be updated, and@@ -104,48 +102,50 @@ -- result. The computation accepts a global parameter that will be -- distributed to all signals.  For instance, this can be the time -- step, if we want to model continuous-time signals.-start :: SignalGen p (Signal p a) -- ^ the generator of the top-level signal-      -> IO (p -> IO a)           -- ^ the computation to sample the signal+start :: SignalGen p (Signal a) -- ^ the generator of the top-level signal+      -> IO (p -> IO a)         -- ^ the computation to sample the signal start (SG gen) = do   pool <- newIORef []-  (S sample) <- gen pool+  (inp,sink) <- external undefined+  S sample <- gen pool inp    ptrs0 <- readIORef pool   writeIORef pool []   (as0,cs0) <- unzip . map fromJust <$> mapM deRefWeak ptrs0-  let ageStatic param = mapM_ ($param) as0+  let ageStatic = sequence_ as0       commitStatic = sequence_ cs0    return $ \param -> do     let update [] ptrs age commit = do           writeIORef pool ptrs-          ageStatic param >> age+          ageStatic >> age           commitStatic >> commit         update (p:ps) ptrs age commit = do           r <- deRefWeak p           case r of             Nothing -> update ps ptrs age commit-            Just (a,c) -> update ps (p:ptrs) (age >> a param) (commit >> c)+            Just (a,c) -> update ps (p:ptrs) (age >> a) (commit >> c) -    res <- sample param+    sink param+    res <- sample     ptrs <- readIORef pool     update ptrs [] (return ()) (return ())     return res  -- | Auxiliary function used by all the primitives that create a -- mutable variable.-addSignal :: (p -> Phase s a -> IO a)  -- ^ sampling function-          -> (p -> Phase s a -> IO ()) -- ^ aging function-          -> IORef (Phase s a)         -- ^ the mutable variable behind the signal-          -> IORef (UpdatePool p)      -- ^ the pool of update actions-          -> IO (Signal p a)+addSignal :: (Phase s a -> IO a)  -- ^ sampling function+          -> (Phase s a -> IO ()) -- ^ aging function+          -> IORef (Phase s a)    -- ^ the mutable variable behind the signal+          -> IORef UpdatePool     -- ^ the pool of update actions+          -> IO (Signal a) addSignal sample age ref pool = do   let  commit (Aged s _)  = Ready s        commit _           = error "commit error: signal not aged" -       sig = S $ \p -> readIORef ref >>= sample p+       sig = S $ readIORef ref >>= sample -  update <- mkWeak sig (\p -> readIORef ref >>= age p, modifyIORef ref commit) Nothing+  update <- mkWeak sig (readIORef ref >>= age, modifyIORef ref commit) Nothing   modifyIORef pool (update:)   return sig @@ -153,63 +153,92 @@ -- the previous superstep, starting with the filler value given in the -- first argument. delay :: a                        -- ^ initial output-      -> Signal p a               -- ^ the signal to delay-      -> SignalGen p (Signal p a)-delay x0 (S s) = SG $ \pool -> do+      -> Signal a                 -- ^ the signal to delay+      -> SignalGen p (Signal a)+delay x0 (S s) = SG $ \pool _ -> do   ref <- newIORef (Ready x0) -  let  sample _ (Ready x)   = return x-       sample _ (Aged _ x)  = return x+  let  sample (Ready x)   = return x+       sample (Aged _ x)  = return x -       age p (Ready x)  = s p >>= \x' -> x' `seq` writeIORef ref (Aged x' x)-       age _ _          = return ()+       age (Ready x)  = s >>= \x' -> x' `seq` writeIORef ref (Aged x' x)+       age _          = return ()    addSignal sample age ref pool  -- | Memoising combinator.  It can be used to cache results of -- applicative combinators in case they are used in several -- places. Other than that, it is equivalent to 'return'.-memo :: Signal p a               -- ^ signal to memoise-     -> SignalGen p (Signal p a)-memo (S s) = SG $ \pool -> do+memo :: Signal a               -- ^ signal to memoise+     -> SignalGen p (Signal a)+memo (S s) = SG $ \pool _ -> do   ref <- newIORef (Ready undefined) -  let  sample p (Ready _)      = s p >>= \x -> writeIORef ref (Aged undefined x) >> return x-       sample _ (Aged _ x)     = return x+  let  sample (Ready _)      = s >>= \x -> writeIORef ref (Aged undefined x) >> return x+       sample (Aged _ x)     = return x -       age p (Ready _)      = s p >>= \x -> writeIORef ref (Aged undefined x)-       age _ _              = return ()+       age (Ready _)      = s >>= \x -> writeIORef ref (Aged undefined x)+       age _              = return ()    addSignal sample age ref pool  -- | A reactive signal that takes the value to output from a monad -- carried by its input.  It is possible to create new signals in the -- monad.-generator :: Signal p (SignalGen p a) -- ^ a stream of generators to potentially run-          -> SignalGen p (Signal p a)-generator (S gen) = SG $ \pool -> do+generator :: Signal (SignalGen p a) -- ^ a stream of generators to potentially run+          -> SignalGen p (Signal a)+generator (S gen) = SG $ \pool inp -> do   ref <- newIORef (Ready undefined) -  let  next p = ($pool).unSG =<< gen p+  let  next = ($inp).($pool).unSG =<< gen -       sample p (Ready _)  = next p >>= \x' -> writeIORef ref (Aged x' x') >> return x'-       sample _ (Aged _ x) = return x+       sample (Ready _)  = next >>= \x' -> writeIORef ref (Aged x' x') >> return x'+       sample (Aged _ x) = return x -       age p (Ready _) = next p >>= \x' -> writeIORef ref (Aged x' x')-       age _ _         = return ()+       age (Ready _) = next >>= \x' -> writeIORef ref (Aged x' x')+       age _         = return ()    addSignal sample age ref pool +-- | A signal that is true exactly once: the first time the input+-- signal is true.  Afterwards, it is constantly false, and it holds+-- no reference to the input signal.+until :: Signal Bool               -- ^ the boolean input signal+      -> SignalGen p (Signal Bool) -- ^ a one-shot signal true only the first time the input is true+until (S s) = SG $ \pool _ -> do+  ref <- newIORef (Ready undefined)++  rsmp <- mfix $ \rs -> newIORef $ do+    x <- s+    writeIORef ref (Aged undefined x)+    when x $ writeIORef rs $ do+      writeIORef ref (Aged undefined False)+      return False+    return x++  let sample = join (readIORef rsmp)++  addSignal (const sample) (const (() <$ sample)) ref pool++-- | The common input signal that is fed through the function returned+-- by 'start', unless we are in an 'embed'ded generator.+input :: SignalGen p (Signal p)+input = SG $ const return++-- | Embed a generator with an overridden input signal.+embed :: Signal p' -> SignalGen p' a -> SignalGen p a+embed s (SG g) = SG $ \pool _ -> g pool s+ -- | A signal that can be directly fed through the sink function -- returned.  This can be used to attach the network to the outer -- world.  Note that this is optional, as all the input of the network -- can be fed in through the global parameter, although that is not -- really convenient for many signals.-external :: a                           -- ^ initial value-         -> IO (Signal p a, a -> IO ()) -- ^ the signal and an IO function to feed it+external :: a                         -- ^ initial value+         -> IO (Signal a, a -> IO ()) -- ^ the signal and an IO function to feed it external x = do   ref <- newIORef x-  return (S (const (readIORef ref)), writeIORef ref)+  return (S (readIORef ref), writeIORef ref)  -- | An event-like signal that can be fed through the sink function -- returned.  The signal carries a list of values fed in since the@@ -219,12 +248,12 @@ -- 'external' this function only returns a generator to be used within -- the expression constructing the top-level stream, and this -- generator can only be used once.-externalMulti :: IO (SignalGen p (Signal p [a]), a -> IO ()) -- ^ a generator for the event signal and the associated sink+externalMulti :: IO (SignalGen p (Signal [a]), a -> IO ()) -- ^ a generator for the event signal and the associated sink externalMulti = do   var <- newMVar []-  return (SG $ \pool -> do-             let sig = S $ const (readMVar var)-             update <- mkWeak sig (const (return ()),takeMVar var >> putMVar var []) Nothing+  return (SG $ \pool _ -> do+             let sig = S $ readMVar var+             update <- mkWeak sig (return (),takeMVar var >> putMVar var []) Nothing              modifyIORef pool (update:)              return sig          ,\val -> do  vals <- takeMVar var@@ -233,68 +262,64 @@  -- | A pure stateful signal.  The initial state is the first output, -- and every following output is calculated from the previous one and--- the value of the global parameter.-stateful :: a -> (p -> a -> a) -> SignalGen p (Signal p a)-stateful x0 f = SG $ \pool -> do-  ref <- newIORef (Ready x0)--  let  sample _ (Ready x)  = return x-       sample _ (Aged _ x) = return x--       age p (Ready x) = let x' = f p x in x' `seq` writeIORef ref (Aged x' x)-       age _ _         = return ()--  addSignal sample age ref pool+-- the value of the global parameter (which might have been overridden+-- by 'embed').  It is equivalent to the following expression:+--+-- @+--  stateful x0 f = 'mfix' $ \sig -> 'input' >>= \i -> 'delay' x0 (f '<$>' i '<*>' sig)+-- @+stateful :: a                    -- ^ initial state+         -> (p -> a -> a)        -- ^ state transformation+         -> SignalGen p (Signal a)+stateful x0 f = mfix $ \sig -> input >>= \i -> delay x0 (f <$> i <*> sig)  -- | A stateful transfer function.  The current input affects the -- current output, i.e. the initial state given in the first argument -- is considered to appear before the first output, and can never be -- observed.  Every output is derived from the current value of the--- input signal, the global parameter and the previous output.-transfer :: a -> (p -> t -> a -> a) -> Signal p t -> SignalGen p (Signal p a)-transfer x0 f (S s) = SG $ \pool -> do-  ref <- newIORef (Ready x0)--  let  sample p (Ready x)  = s p >>= \y -> let x' = f p y x in-                                            x' `seq` writeIORef ref (Aged x' x') >> return x'-       sample _ (Aged _ x) = return x--       age p (Ready x) = s p >>= \y -> let x' = f p y x in-                                        x' `seq` writeIORef ref (Aged x' x')-       age _ _         = return ()--  addSignal sample age ref pool+-- input signal, the global parameter (which might have been+-- overridden by 'embed') and the previous output.  It is equivalent+-- to the following expression:+--+-- @+--  transfer x0 f s = 'mfix' $ \sig -> 'input' >>= \i -> 'liftA3' f i s '<$>' 'delay' x0 sig+-- @+transfer :: a                    -- ^ initial internal state+         -> (p -> t -> a -> a)   -- ^ state updater function+         -> Signal t             -- ^ input signal+         -> SignalGen p (Signal a)+transfer x0 f s = mfix $ \sig -> input >>= \i -> liftA3 f i s <$> delay x0 sig  -- | A random signal.-noise :: MTRandom a => SignalGen p (Signal p a)-noise = memo (S (const randomIO))+noise :: MTRandom a => SignalGen p (Signal a)+noise = memo (S randomIO)  -- | A random source within the 'SignalGen' monad. getRandom :: MTRandom a => SignalGen p a-getRandom = SG (const randomIO)+getRandom = SG (const (const randomIO))  -- | A printing action within the 'SignalGen' monad. debug :: String -> SignalGen p ()-debug = SG . const . putStrLn+debug = SG . const . const . putStrLn  -- | The @Show@ instance is only defined for the sake of 'Num'...-instance Show (Signal p a) where+instance Show (Signal a) where   showsPrec _ _ s = "<SIGNAL>" ++ s  -- | Equality test is impossible.-instance Eq (Signal p a) where+instance Eq (Signal a) where   _ == _ = False  -- | Error message for unimplemented instance functions. unimp :: String -> a unimp = error . ("Signal: "++) -instance Ord t => Ord (Signal p t) where+instance Ord t => Ord (Signal t) where   compare = unimp "compare"   min = liftA2 min   max = liftA2 max -instance Enum t => Enum (Signal p t) where+instance Enum t => Enum (Signal t) where   succ = fmap succ   pred = fmap pred   toEnum = pure . toEnum@@ -304,11 +329,11 @@   enumFromTo = unimp "enumFromTo"   enumFromThenTo = unimp "enumFromThenTo" -instance Bounded t => Bounded (Signal p t) where+instance Bounded t => Bounded (Signal t) where   minBound = pure minBound   maxBound = pure maxBound -instance Num t => Num (Signal p t) where+instance Num t => Num (Signal t) where   (+) = liftA2 (+)   (-) = liftA2 (-)   (*) = liftA2 (*)@@ -317,10 +342,10 @@   negate = fmap negate   fromInteger = pure . fromInteger -instance Real t => Real (Signal p t) where+instance Real t => Real (Signal t) where   toRational = unimp "toRational" -instance Integral t => Integral (Signal p t) where+instance Integral t => Integral (Signal t) where   quot = liftA2 quot   rem = liftA2 rem   div = liftA2 div@@ -331,12 +356,12 @@     where dmab = divMod <$> a <*> b   toInteger = unimp "toInteger" -instance Fractional t => Fractional (Signal p t) where+instance Fractional t => Fractional (Signal t) where   (/) = liftA2 (/)   recip = fmap recip   fromRational = pure . fromRational -instance Floating t => Floating (Signal p t) where+instance Floating t => Floating (Signal t) where   pi = pure pi   exp = fmap exp   sqrt = fmap sqrt
FRP/Elerea/Simple.hs view
@@ -131,10 +131,12 @@     , delay     , generator     , memo+    , until     , stateful     , transfer     , noise     , getRandom+    , debug     ) where  import Control.Applicative@@ -143,6 +145,7 @@ import Control.Monad.Fix import Data.IORef import Data.Maybe+import Prelude hiding (until) import System.Mem.Weak import System.Random.Mersenne @@ -273,7 +276,7 @@                    writeIORef ref (Updated undefined x)                    return x -  addSignal (const sample) (const (sample >> return ())) ref pool+  addSignal (const sample) (const (() <$ sample)) ref pool  -- | Memoising combinator.  It can be used to cache results of -- applicative combinators in case they are used in several places.@@ -286,8 +289,36 @@    let sample = s >>= \x -> writeIORef ref (Updated undefined x) >> return x -  addSignal (const sample) (const (sample >> return ())) ref pool+  addSignal (const sample) (const (() <$ sample)) ref pool +-- | A signal that is true exactly once: the first time the input+-- signal is true.  Afterwards, it is constantly false, and it holds+-- no reference to the input signal.  It is observationally equivalent+-- to the following expression (which would hold onto @s@ forever):+--+-- @+--  until s = do+--    step <- 'transfer' False (||) s+--    dstep <- 'delay' False step+--    return $ 'liftA2' (/=) step dstep+-- @+until :: Signal Bool             -- ^ the boolean input signal+      -> SignalGen (Signal Bool) -- ^ a one-shot signal true only the first time the input is true+until (S s) = SG $ \pool -> do+  ref <- newIORef (Ready undefined)++  rsmp <- mfix $ \rs -> newIORef $ do+    x <- s+    writeIORef ref (Updated undefined x)+    when x $ writeIORef rs $ do+      writeIORef ref (Updated undefined False)+      return False+    return x++  let sample = join (readIORef rsmp)++  addSignal (const sample) (const (() <$ sample)) ref pool+ -- | A signal that can be directly fed through the sink function -- returned.  This can be used to attach the network to the outer -- world.@@ -354,6 +385,10 @@ -- | A random source within the 'SignalGen' monad. getRandom :: MTRandom a => SignalGen a getRandom = SG (const randomIO)++-- | A printing action within the 'SignalGen' monad.+debug :: String -> SignalGen ()+debug = SG . const . putStrLn  -- The Show instance is only defined for the sake of Num... instance Show (Signal a) where
elerea.cabal view
@@ -1,11 +1,11 @@ Name:                elerea-Version:             2.0.0+Version:             2.1.0 Cabal-Version:       >= 1.2 Synopsis:            A minimalistic FRP library Category:            reactivity, FRP Description: - Elerea (Eventless reactivity) is a tiny continuous-time FRP+ Elerea (Eventless reactivity) is a tiny discrete time FRP  implementation without the notion of event-based switching and  sampling, with first-class signals (time-varying values).  Reactivity  is provided through various higher-order constructs that also allow@@ -15,15 +15,28 @@  Stateful signals can be safely generated at any time through a  specialised monad, while stateless combinators can be used in a  purely applicative style.  Elerea signals can be defined recursively,- and external input is trivial to attach.  A unique feature of the- library is that cyclic dependencies are detected on the fly and- resolved by inserting delays dynamically, unless the user does it- explicitly.+ and external input is trivial to attach.  The library comes in four+ major variants:  .+ * Simple: signals are plain discrete streams isomorphic to functions+   over natural numbers;+ .+ * Param: adds a globally accessible input signal for convenience;+ .+ * Clocked: adds the ability to freeze whole subnetworks at will;+ .+ * Delayed: attempts to resolve instantaneous dependency cycles+   (i.e. cycles without a delay); this variant is likely to be+   deprecated in the near future.+ .+ The first three variants come with precise denotational semantics.+ .  This is a minimal library that defines only some basic primitives,  and you are advised to install @elerea-examples@ as well to get an  idea how to build non-trivial systems with it.  The examples are  separated in order to minimise the dependencies of the core library.+ The @dow@ package contains a full game built on top of the simple+ variant.  Author:              Patai Gergely Maintainer:          Patai Gergely (patai@iit.bme.hu)