packages feed

elerea 0.6.0 → 1.0.0

raw patch · 5 files changed

+217/−141 lines, 5 filesdep ~basePVP ok

version bump matches the API change (PVP)

Dependency ranges changed: base

API changes (from Hackage documentation)

- FRP.Elerea: (==>) :: StartToken -> a -> a
- FRP.Elerea: data StartToken
- FRP.Elerea: latcher :: Signal a -> Signal Bool -> Signal (Signal a) -> Signal a
- FRP.Elerea: startTokens :: Signal StartToken
- FRP.Elerea.Internal: (==>) :: StartToken -> a -> a
- FRP.Elerea.Internal: SNL :: (Signal a) -> (Signal Bool) -> (Signal (Signal a)) -> SignalNode a
- FRP.Elerea.Internal: SNU :: SignalNode a
- FRP.Elerea.Internal: data StartToken
- FRP.Elerea.Internal: latcher :: Signal a -> Signal Bool -> Signal (Signal a) -> Signal a
- FRP.Elerea.Internal: startTokens :: Signal StartToken
+ FRP.Elerea: createSignal :: SignalMonad a -> IO a
+ FRP.Elerea: data SignalMonad a
+ FRP.Elerea: generator :: Signal Bool -> Signal (SignalMonad a) -> Signal (Maybe a)
+ FRP.Elerea: sampler :: Signal (Signal a) -> Signal a
+ FRP.Elerea: signalDebug :: (Show a) => a -> SignalMonad ()
+ FRP.Elerea: storeJust :: a -> Signal (Maybe a) -> SignalMonad (Signal a)
+ FRP.Elerea: toMaybe :: Bool -> a -> Maybe a
+ FRP.Elerea.Internal: SM :: IO a -> SignalMonad a
+ FRP.Elerea.Internal: SNH :: (Signal (Signal a)) -> (IORef (Signal a)) -> SignalNode a
+ FRP.Elerea.Internal: SNM :: (Signal Bool) -> (Signal (SignalMonad a)) -> SignalNode a
+ FRP.Elerea.Internal: generator :: Signal Bool -> Signal (SignalMonad a) -> Signal (Maybe a)
+ FRP.Elerea.Internal: instance (Bounded t) => Bounded (Signal t)
+ FRP.Elerea.Internal: instance (Enum t) => Enum (Signal t)
+ FRP.Elerea.Internal: instance (Integral t) => Integral (Signal t)
+ FRP.Elerea.Internal: instance (Ord t) => Ord (Signal t)
+ FRP.Elerea.Internal: instance (Real t) => Real (Signal t)
+ FRP.Elerea.Internal: instance Applicative SignalMonad
+ FRP.Elerea.Internal: instance Functor SignalMonad
+ FRP.Elerea.Internal: instance Monad SignalMonad
+ FRP.Elerea.Internal: instance MonadFix SignalMonad
+ FRP.Elerea.Internal: makeSignal :: SignalNode a -> SignalMonad (Signal a)
+ FRP.Elerea.Internal: makeSignalUnsafe :: SignalNode a -> Signal a
+ FRP.Elerea.Internal: newtype SignalMonad a
+ FRP.Elerea.Internal: sampler :: Signal (Signal a) -> Signal a
+ FRP.Elerea.Internal: signalDebug :: (Show a) => a -> SignalMonad ()
+ FRP.Elerea.Internal: toMaybe :: Bool -> a -> Maybe a
+ FRP.Elerea.Internal: unimp :: String -> a
- FRP.Elerea: delay :: a -> Signal a -> Signal a
+ FRP.Elerea: delay :: a -> Signal a -> SignalMonad (Signal a)
- FRP.Elerea: edge :: Signal Bool -> Signal Bool
+ FRP.Elerea: edge :: Signal Bool -> SignalMonad (Signal Bool)
- FRP.Elerea: stateful :: a -> (DTime -> a -> a) -> Signal a
+ FRP.Elerea: stateful :: a -> (DTime -> a -> a) -> SignalMonad (Signal a)
- FRP.Elerea: transfer :: a -> (DTime -> t -> a -> a) -> Signal t -> Signal a
+ FRP.Elerea: transfer :: a -> (DTime -> t -> a -> a) -> Signal t -> SignalMonad (Signal a)
- FRP.Elerea.Internal: createSignal :: SignalNode a -> Signal a
+ FRP.Elerea.Internal: createSignal :: SignalMonad a -> IO a
- FRP.Elerea.Internal: delay :: a -> Signal a -> Signal a
+ FRP.Elerea.Internal: delay :: a -> Signal a -> SignalMonad (Signal a)
- FRP.Elerea.Internal: stateful :: a -> (DTime -> a -> a) -> Signal a
+ FRP.Elerea.Internal: stateful :: a -> (DTime -> a -> a) -> SignalMonad (Signal a)
- FRP.Elerea.Internal: transfer :: a -> (DTime -> t -> a -> a) -> Signal t -> Signal a
+ FRP.Elerea.Internal: transfer :: a -> (DTime -> t -> a -> a) -> Signal t -> SignalMonad (Signal a)

Files

CHANGES view
@@ -1,3 +1,6 @@+1.0.0 - 090726+* completely renewed interface by introducing the SignalMonad+ 0.6.0 - 090507 * renamed Void to StartToken * replaced restarter with the simpler and more versatile startTokens
FRP/Elerea.hs view
@@ -1,22 +1,26 @@ {-|  Elerea (Eventless Reactivity) is a simplistic FRP implementation that-parts with the concept of events, and uses a continuous latching-construct instead. The user sees the functionality through an-applicative interface, which is used to build up a network of-interconnected mutable references. The network is executed-iteratively, where each superstep consists of two phases:-sampling-aging and finalisation.  As an example, the following code is-a possible way to define an approximation of our beloved trig-functions:+parts with the concept of events, and introduces various constructs+that can be used to define completely dynamic higher-order dataflow+networks.  The user sees the functionality through a hybrid+monadic-applicative interface, where stateful signals can only be+created through a specialised monad, while most combinators are purely+applicative.  The combinators build up a network of interconnected+mutable references in the background.  The network is executed+iteratively, where each superstep consists of three phases: sampling,+aging, and finalisation.  As an example, the following code is a+possible way to define an approximation of our beloved trig functions:  @- sine = integral 0 cosine- cosine = integral 1 (-sine)+ (sine,cosine) <- mdo+   s <- integral 0 c+   c <- integral 1 (-s)+   return (s,c) @  Note that @integral@ is not a primitive, it can be defined by the user-as a transfer function. A possible implementation that can be used on+as a transfer function.  A possible implementation that can be used on any 'Fractional' signal looks like this:  @@@ -29,14 +33,18 @@  -} -module FRP.Elerea (-  DTime, Sink, Signal, StartToken,-  superstep, external, keepAlive, (.@.),-  stateful, transfer, latcher,-  delay, startTokens, (==>),-  edge,-  (==@), (/=@), (<@), (<=@), (>=@), (>@),-  (&&@), (||@)+module FRP.Elerea+    ( DTime, Sink, Signal, SignalMonad+    , createSignal, superstep+    , external+    , stateful, transfer, delay+    , sampler, generator+    , storeJust, toMaybe+    , edge+    , keepAlive, (.@.)+    , (==@), (/=@), (<@), (<=@), (>=@), (>@)+    , (&&@), (||@)+    , signalDebug ) where  import Control.Applicative@@ -55,8 +63,19 @@ bool signal that turns true only at the moment when there is a rising edge on the input. -} -edge :: Signal Bool -> Signal Bool-edge b = (not <$> delay True b) &&@ b+edge :: Signal Bool -> SignalMonad (Signal Bool)+edge b = delay True b >>= \db -> return $ (not <$> db) &&@ b++{-| The `storeJust` transfer function behaves as a latch on a 'Maybe'+input: it keeps its state when the input is 'Nothing', and replaces it+with the input otherwise. -}++storeJust :: a                      -- ^ Initial output+          -> Signal (Maybe a)       -- ^ Maybe signal to latch on+          -> SignalMonad (Signal a)+storeJust x0 s = transfer x0 store s+    where store _ Nothing  x = x+          store _ (Just x) _ = x  {-| Point-wise equality of two signals. -} 
FRP/Elerea/Graph.hs view
@@ -26,10 +26,10 @@     | Stateful     | Transfer Id     | App Id Id-    | Latcher Id Id Id+    | Sampler Id+    | Generator Id Id     | External     | Delay Id-    | Tokens     | Lift1 Id     | Lift2 Id Id     | Lift3 Id Id Id@@ -59,16 +59,17 @@   (sf',st') <- buildStore (Map.insert p None st) sf   (sx',st'') <- buildStore st' sx   return (Map.insert p (App sf' sx') st'')-insertSignal st p (SNL s e ss) = do-  (s',st') <- buildStore (Map.insert p None st) s-  (e',st'') <- buildStore st' e-  (ss',st''') <- buildStore st'' ss-  return (Map.insert p (Latcher s' e' ss') st''')+insertSignal st p (SNH ss _) = do+  (ss',st') <- buildStore (Map.insert p None st) ss+  return (Map.insert p (Sampler ss') st')+insertSignal st p (SNM b sm) = do+  (b',st') <- buildStore (Map.insert p None st) b+  (sm',st'') <- buildStore st' sm+  return (Map.insert p (Generator b' sm') st'') insertSignal st p (SNE _) = return (Map.insert p External st) insertSignal st p (SND _ s) = do   (s',st') <- buildStore (Map.insert p None st) s   return (Map.insert p (Delay s') st')-insertSignal st p (SNU) = return (Map.insert p Tokens st) insertSignal st p (SNKA (S r) _) = do   Ready s <- readIORef r   insertSignal st p s@@ -104,9 +105,9 @@                       Stateful        -> "stateful"                       Transfer _      -> "transfer"                       App _ _         -> "app"-                      Latcher _ _ _   -> "latcher"+                      Sampler _       -> "sampler"+                      Generator _ _   -> "generator"                       External        -> "external"-                      Tokens          -> "tokens"                       Delay _         -> "delay"                       Lift1 _         -> "fun1"                       Lift2 _ _       -> "fun2"@@ -123,10 +124,9 @@ (<http://www.graphviz.org/>) dot format.  Stateful nodes are coloured according to their type. -Because of the fact that Elerea primitives are not referentially-transparent, the results might differ depending on whether this-function is called before or after sampling (this also affects the-actual network!), but the networks should be still equivalent.+The results might differ depending on whether this function is called+before or after sampling (this also affects the actual network!), but+the networks should be still equivalent.  -} @@ -140,7 +140,9 @@                 edges = case n of                   Transfer s           -> mkEdge s "\"\""                   App sf sx            -> mkEdge sf "f" ++ mkEdge sx "x"-                  Latcher s e ss       -> mkEdge s "init" ++ mkEdge e  "ctl" ++ mkEdge ss "\"\""+                  Sampler ss           -> mkEdge ss "\"\""+                  Generator b sm       -> mkEdge b "ctl" ++ mkEdge sm "gen"+                  Delay s              -> mkEdge s "\"\""                   Lift1 s1             -> mkEdge s1 "x1"                   Lift2 s1 s2          -> mkEdge s1 "x1" ++ mkEdge s2 "x2"                   Lift3 s1 s2 s3       -> mkEdge s1 "x1" ++ mkEdge s2 "x2" ++ mkEdge s3 "x3"@@ -153,16 +155,17 @@                 mkLabel name attrs = " [label=" ++ name ++ "," ++ attrs ++ "];\n"                 nodeCol = case n of                   Transfer _    -> "ffcc99"-                  Latcher _ _ _ -> "99ccff"+                  Sampler _     -> "99ccff"+                  Generator _ _ -> "ccffff"                   External      -> "ccff99"                   Stateful      -> "ffffcc"                   Delay _       -> "ffccff"                   _             -> "ffffff"                 nodeShape = case n of                   Transfer _    -> "diamond"-                  Latcher _ _ _ -> "hexagon"+                  Sampler _     -> "hexagon"+                  Generator _ _ -> "house"                   External      -> "invtriangle"                   Delay _       -> "box"-                  Tokens        -> "house"                   _             -> "ellipse"   return $ "digraph G {\n" ++ concat rules ++ "}\n"
FRP/Elerea/Internal.hs view
@@ -1,10 +1,10 @@-{-# LANGUAGE ExistentialQuantification, EmptyDataDecls #-}+{-# LANGUAGE ExistentialQuantification, GeneralizedNewtypeDeriving #-} {-# OPTIONS_GHC -fno-warn-name-shadowing #-}  {-|  This is the core module of Elerea, which contains the signal-implementation and the primitive constructors.+implementation and the atomic constructors.  The basic idea is to create a dataflow network whose structure closely resembles the user's definitions by turning each combinator into a@@ -16,14 +16,18 @@ references: one to the function signal and another to the argument signal. -In order to have a pure(-looking) applicative interface, the library-relies on 'unsafePerformIO' to create the references on demand.  In-contrast, the execution of the network is explicitly marked as an IO-operation.  The core library exposes a single function to animate the-network called 'superstep', which takes a signal and a time interval,-and mutates all the variables the signal depends on.  It is supposed-to be called repeatedly in a loop that also takes care of user input.+In order to have a pure(-looking) applicative interface for the most+part, the library relies on 'unsafePerformIO' to create the references+of stateless signals, while stateful signals have to be obtained from+a special 'SignalMonad', which is just a wrapping of 'IO' that doesn't+allow any other action to be performed. +The execution of the network is explicitly marked as an IO operation.+The core library exposes a single function to animate the network+called 'superstep', which takes a signal and a time interval, and+mutates all the variables the signal depends on.  It is supposed to be+called repeatedly in a loop that also takes care of user input.+ To ensure consistency, a superstep has three phases: sampling, aging and finalisation.  Each signal reachable from the top-level signal passed to 'superstep' is sampled at the current point of time@@ -54,6 +58,7 @@  import Control.Applicative import Control.Monad+import Control.Monad.Fix import Data.IORef import System.IO.Unsafe @@ -69,15 +74,20 @@  type Sink a = a -> IO () -{-| An empty type to use as a token for injecting data-dependencies. -}+-- ** The data structures behind signals -data StartToken+{-| A restricted monad to create stateful signals in. -} --- ** The data structures behind signals+newtype SignalMonad a = SM { createSignal :: IO a } deriving (Monad,Applicative,Functor,MonadFix) -{-| A signal is represented as a /transactional/ structural node. -}+{-| A printing function that can be used in the 'SignalMonad'.+Provided for debugging purposes. -} +signalDebug :: Show a => a -> SignalMonad ()+signalDebug = SM . print++{-| A signal is conceptually a time-varying value. -}+ newtype Signal a = S (IORef (SignalTrans a))  {-| A node can have four states that distinguish various stages of@@ -86,8 +96,8 @@ data SignalTrans a     -- | @Ready s@ is simply the signal @s@ that was not sampled yet     = Ready (SignalNode a)-    -- | @Sampling s@ is still @s@ after its current value was-    -- requested, but still not delivered+    -- | @Sampling s@ is signal @s@ after its current value was+    -- requested, but not yet delivered     | Sampling (SignalNode a)     -- | @Sampled x s@ is signal @s@ paired with its current value @x@     | Sampled a (SignalNode a)@@ -111,15 +121,18 @@     | forall t . SNT (Signal t) a (DTime -> t -> a -> a)     -- | @SNA sf sx@: pointwise function application     | forall t . SNA (Signal (t -> a)) (Signal t)-    -- | @SNL s e ss@: latcher that starts out as @s@ and becomes the-    -- current value of @ss@ at every moment when @e@ is true-    | SNL (Signal a) (Signal Bool) (Signal (Signal a))+    -- | @SNH ss r@: the higher-order signal @ss@ collapsed into a+    -- signal cached in reference @r@; @r@ is used during the aging+    -- phase+    | SNH (Signal (Signal a)) (IORef (Signal a))+    -- | @SNM b sm@: signal generator that executes the monad carried+    -- by @sm@ whenever @b@ is true, and outputs the result (or+    -- undefined when @b@ is false)+    | SNM (Signal Bool) (Signal (SignalMonad a))     -- | @SNE r@: opaque reference to connect peripherals     | SNE (IORef a)     -- | @SND s@: the @s@ signal delayed by one superstep     | SND a (Signal a)-    -- | @SNU@: a stream of unique identifiers for each superstep-    | SNU     -- | @SNKA s l@: equivalent to @s@ while aging signal @l@     | forall t . SNKA (Signal a) (Signal t)     -- | @SNF1 f@: @fmap f@@@ -151,7 +164,7 @@  instance Applicative Signal where     -- | A constant signal-    pure = createSignal . SNK+    pure = makeSignalUnsafe . SNK     -- | Point-wise application of a function and a data signal (like @ZipList@)     f@(S rf) <*> x@(S rx) = unsafePerformIO $ do       -- General fall-back case@@ -163,10 +176,10 @@       -- so we need to prepare to meeting undefined references by       -- wrapping reads into exception handlers. -      flip catch (const (return ())) $ do+      flip catch (const (debugLog "no_fun" $ return ())) $ do         Ready nf <- readIORef rf -        merged <- flip catch (const (return False)) $ do+        merged <- flip catch (const (debugLog "no_arg" $ return False)) $ do           -- Merging constant branches from the two sides           Ready nx <- readIORef rx           case (nf,nx) of@@ -221,6 +234,30 @@ instance Eq (Signal a) where     S s1 == S s2 = s1 == s2 +{-| Error message for unimplemented instance functions. -}++unimp :: String -> a+unimp = error . ("Signal: "++)++instance Ord t => Ord (Signal t) where+    compare = unimp "compare"+    min = liftA2 min+    max = liftA2 max++instance Enum t => Enum (Signal t) where+    succ = fmap succ+    pred = fmap pred+    toEnum = pure . toEnum+    fromEnum = unimp "fromEnum"+    enumFrom = unimp "enumFrom"+    enumFromThen = unimp "enumFromThen"+    enumFromTo = unimp "enumFromTo"+    enumFromThenTo = unimp "enumFromThenTo"++instance Bounded t => Bounded (Signal t) where+    minBound = pure minBound+    maxBound = pure maxBound+ instance Num t => Num (Signal t) where     (+) = liftA2 (+)     (-) = liftA2 (-)@@ -230,6 +267,20 @@     negate = fmap negate     fromInteger = pure . fromInteger +instance Real t => Real (Signal t) where+    toRational = unimp "toRational"++instance Integral t => Integral (Signal t) where+    quot = liftA2 quot+    rem = liftA2 rem+    div = liftA2 div+    mod = liftA2 mod+    quotRem a b = (fst <$> qrab,snd <$> qrab)+        where qrab = quotRem <$> a <*> b+    divMod a b = (fst <$> dmab,snd <$> dmab)+        where dmab = divMod <$> a <*> b+    toInteger = unimp "toInteger"+ instance Fractional t => Fractional (Signal t) where     (/) = liftA2 (/)     recip = fmap recip@@ -257,12 +308,20 @@  -- ** Internal functions to run the network -{-| This function is really just a shorthand to create a reference to-a given node. -}+{-| Creating a reference within the 'SignalMonad'.  Used for stateful+signals. -} -createSignal :: SignalNode a -> Signal a-createSignal = S . unsafePerformIO . newIORef . Ready+makeSignal :: SignalNode a -> SignalMonad (Signal a)+makeSignal node = SM $ do+  ref <- newIORef (Ready node)+  return (S ref) +{-| Creating a reference as a pure value.  Used for stateless+signals. -}++makeSignalUnsafe :: SignalNode a -> Signal a+makeSignalUnsafe = S . unsafePerformIO . newIORef . Ready+ {-| Sampling the signal and all of its dependencies, at the same time. We don't need the aged signal in the current superstep, only the current value, so we sample before propagating the changes, which@@ -309,7 +368,8 @@                       case s' of                         SNT s _ _             -> age s dt                         SNA sf sx             -> age sf dt >> age sx dt-                        SNL s e ss            -> age s dt >> age e dt >> age ss dt+                        SNH ss r              -> age ss dt >> readIORef r >>= \s -> age s dt+                        SNM b sm              -> age b dt >> age sm dt                         SND _ s               -> age s dt                          SNKA s l              -> age s dt >> age l dt                         SNF1 _ s              -> age s dt @@ -332,7 +392,8 @@                    case s of                      SNT s _ _             -> commit s                      SNA sf sx             -> commit sf >> commit sx-                     SNL s e ss            -> commit s >> commit e >> commit ss+                     SNH ss r              -> commit ss >> readIORef r >>= \s -> commit s+                     SNM b sm              -> commit b >> commit sm                      SND _ s               -> commit s                       SNKA s l              -> commit s >> commit l                      SNF1 _ s              -> commit s @@ -345,26 +406,20 @@     _           -> error "Inconsistent state: signal not aged properly!"  {-| Aging the signal.  Stateful signals have their state forced to-prevent building up big thunks, and the latcher also does its job-here.  The other nodes are structurally static. -}+prevent building up big thunks.  The other nodes are structurally+static. -}  advance :: SignalNode a -> a -> DTime -> IO (SignalNode a)-advance (SNS x f)       _ dt = x `seq` return (SNS (f dt x) f)-advance (SNT s _ f)     v _  = v `seq` return (SNT s v f)-advance sw@(SNL _ e ss) _ dt = do -- These are ready samples!-                                  b <- signalValue e dt-                                  s' <- signalValue ss dt-                                  if b-                                    then return (SNL s' e ss)-                                    else return sw-advance (SND _ s)       _ dt = do x <- signalValue s dt-                                  return (SND x s)-advance s               _ _  = return s+advance (SNS x f)    _ dt = x `seq` return (SNS (f dt x) f)+advance (SNT s _ f)  v _  = v `seq` return (SNT s v f)+advance (SND _ s)    _ dt = do x <- signalValue s dt+                               return (SND x s)+advance s            _ _  = return s  {-| Sampling the signal at the current moment.  This is where static nodes propagate changes to those they depend on.  Transfer functions-('SNT') and latchers ('SNL') work without delay, i.e. the effects of-their input signals can be observed in the same superstep. -}+('SNT') work without delay, i.e. the effects of their input signals+can be observed in the same superstep. -}  sample :: SignalNode a -> DTime -> IO a sample (SNK x)                 _  = return x@@ -372,12 +427,14 @@ sample (SNT s x f)             dt = do t <- signalValue s dt                                        return $! f dt t x sample (SNA sf sx)             dt = signalValue sf dt <*> signalValue sx dt-sample (SNL s e ss)            dt = do b <- signalValue e dt-                                       s' <- signalValue ss dt-                                       signalValue (if b then s' else s) dt+sample (SNH ss r)              dt = do s <- signalValue ss dt+                                       writeIORef r s+                                       signalValue s dt+sample (SNM b sm)              dt = do c <- signalValue b dt+                                       SM m <- signalValue sm dt+                                       if c then m else return undefined sample (SNE r)                 _  = readIORef r sample (SND v _)               _  = return v-sample (SNU)                   _  = return undefined sample (SNKA s l)              dt = do signalValue l dt                                        signalValue s dt sample (SNF1 f s)              dt = f <$> signalValue s dt@@ -388,18 +445,17 @@  {-| Sampling the signal with some kind of delay in order to resolve dependency loops.  Transfer functions simply return their previous-output, while latchers postpone the change and pass through the-current value of their current signal even if the latch control signal-is true at the moment.  Other types of signals are always handled by-the `sample` function, so it is not possible to create a stateful loop-composed of solely stateless combinators. -}+output (delays can be considered a special case, because they always+do that, so 'sampleDelayed' is never called with them), while other+types of signals are always handled by the 'sample' function, so it is+not possible to create a working stateful loop composed of solely+stateless combinators. -}  sampleDelayed :: SignalNode a -> DTime -> IO a sampleDelayed (SNT _ x _) _  = return x-sampleDelayed (SNL s _ _) dt = signalValue s dt sampleDelayed sn          dt = sample sn dt --- ** Userland primitives+-- ** Userland combinators  {-| Advancing the whole network that the given signal depends on by the amount of time given in the second argument. -}@@ -417,8 +473,8 @@  stateful :: a                 -- ^ initial state          -> (DTime -> a -> a) -- ^ state transformation-         -> Signal a-stateful x0 f = createSignal (SNS x0 f)+         -> SignalMonad (Signal a)+stateful x0 f = makeSignal (SNS x0 f)  {-| A stateful transfer function.  The current input affects the current output, i.e. the initial state given in the first argument is@@ -428,21 +484,32 @@ transfer :: a                      -- ^ initial internal state          -> (DTime -> t -> a -> a) -- ^ state updater function          -> Signal t               -- ^ input signal-         -> Signal a-transfer x0 f s = createSignal (SNT s x0 f)+         -> SignalMonad (Signal a)+transfer x0 f s = makeSignal (SNT s x0 f) -{-| Reactive signal that starts out as @s@ and can change its-behaviour to the one supplied in @ss@ whenever @e@ is true.  The-change can be observed immediately, unless the signal is sampled by-`sampleDelayed`, which puts a delay on the latch control (but not on-the latched signal!). -}+{-| A continuous sampler that flattens a higher-order signal by+outputting its current snapshots. -} -latcher :: Signal a          -- ^ @s@: initial behaviour-        -> Signal Bool       -- ^ @e@: latch control signal-        -> Signal (Signal a) -- ^ @ss@: signal of potential future behaviours+sampler :: Signal (Signal a) -- ^ signal to flatten         -> Signal a-latcher s e ss = createSignal (SNL s e ss)+sampler ss = makeSignalUnsafe (SNH ss (unsafePerformIO (newIORef undefined))) +{-| A reactive signal that takes the value to output from a monad+carried by its input when a boolean control signal is true, otherwise+it outputs 'Nothing'.  It is possible to create new signals in the+monad and also to print debug messages. -}++generator :: Signal Bool            -- ^ control (trigger) signal+          -> Signal (SignalMonad a) -- ^ a stream of monads to potentially run+          -> Signal (Maybe a)+generator b sm = toMaybe <$> b <*> makeSignalUnsafe (SNM b sm)++{-| A helper function to wrap any value in a 'Maybe' depending on a+boolean condition. -}++toMaybe :: Bool -> a -> Maybe a+toMaybe c v = if c then Just v else Nothing+ {-| A signal that can be directly fed through the sink function returned.  This can be used to attach the network to the outer world. -}@@ -461,8 +528,8 @@  delay :: a        -- ^ initial output       -> Signal a -- ^ the signal to delay-      -> Signal a-delay x0 s = createSignal (SND x0 s)+      -> SignalMonad (Signal a)+delay x0 s = makeSignal (SND x0 s)  {-| Dependency injection to allow aging signals whose output is not necessarily needed to produce the current sample of the first@@ -472,23 +539,4 @@ keepAlive :: Signal a -- ^ the actual output           -> Signal t -- ^ a signal guaranteed to age when this one is sampled           -> Signal a-keepAlive s l = createSignal (SNKA s l)--{-| A stream of tokens freshly generated in each superstep.  These are-dummy values that must not be evaluated (they are in fact-'undefined'), but distributed among signals that need to be-constructed at the given moment in the absence of other dependencies-on current values, so as to prevent let-floating from moving otherwise-independent signals to an outer scope.  Dependency on these tokens can-be established with the '==>' operator. -}--startTokens :: Signal StartToken-startTokens = createSignal SNU--{-| An operator that ignores its first argument and returns the-second, but hides the fact that the first argument is not needed.  It-is equivalent to @flip const@, but it cannot be inlined. -}--{-# NOINLINE (==>) #-}-(==>) :: StartToken -> a -> a-_ ==> x = x+keepAlive s l = makeSignalUnsafe (SNKA s l)
elerea.cabal view
@@ -1,5 +1,5 @@ Name:                elerea-Version:             0.6.0+Version:             1.0.0 Cabal-Version:       >= 1.2 Synopsis:            A minimalistic FRP library Category:            reactivity, FRP@@ -7,19 +7,22 @@   Elerea (Eventless reactivity) is a tiny continuous-time FRP  implementation without the notion of event-based switching and- sampling, with first-class signals (time-varying values). Reactivity- is provided through a latching mechanism where a signal changes its- behaviour as dictated by a boolean input signal.+ sampling, with first-class signals (time-varying values).  Reactivity+ is provided through various higher-order constructs that also allow+ the user to work with arbitrary time-varying structures containing+ live signals.  .- Elerea provides an easy to use applicative interface, supports- recursive signals (a definition like @sine = integral 0 (integral 1- (-sine))@ works without a hitch) and arbitrary external input. Cyclic- dependencies are detected on the fly and resolved by inserting delays- dynamically, unless the user does it explicitly.+ 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.  .  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+ idea how to build non-trivial systems with it.  The examples are  separated in order to minimise the dependencies of the core library.  Author:              Patai Gergely@@ -38,5 +41,5 @@     FRP.Elerea.Internal     FRP.Elerea.Graph -  Build-Depends:       base, containers+  Build-Depends:       base >= 3 && < 5, containers   ghc-options:         -Wall -O2