Elerea (Eventless Reactivity) is a simplistic FRP implementation that 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,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
any Fractional
signal looks like this:
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.
The FRP.Elerea.Experimental branch provides a similar interface with a rather different underlying structure, which is likely to be more efficient.
- type DTime = Double
- type Sink a = a -> IO ()
- data Signal a
- data SignalMonad a
- createSignal :: SignalMonad a -> IO a
- superstep :: Signal a -> DTime -> IO a
- external :: a -> IO (Signal a, Sink a)
- stateful :: a -> (DTime -> a -> a) -> SignalMonad (Signal a)
- transfer :: a -> (DTime -> t -> a -> a) -> Signal t -> SignalMonad (Signal a)
- delay :: a -> Signal a -> SignalMonad (Signal a)
- sampler :: Signal (Signal a) -> Signal a
- generator :: Signal Bool -> Signal (SignalMonad a) -> Signal (Maybe a)
- storeJust :: a -> Signal (Maybe a) -> SignalMonad (Signal a)
- toMaybe :: Bool -> a -> Maybe a
- edge :: Signal Bool -> SignalMonad (Signal Bool)
- keepAlive :: Signal a -> Signal t -> Signal a
- (.@.) :: Signal a -> Signal t -> Signal a
- (==@) :: Eq a => Signal a -> Signal a -> Signal Bool
- (/=@) :: Eq a => Signal a -> Signal a -> Signal Bool
- (<@) :: Ord a => Signal a -> Signal a -> Signal Bool
- (<=@) :: Ord a => Signal a -> Signal a -> Signal Bool
- (>=@) :: Ord a => Signal a -> Signal a -> Signal Bool
- (>@) :: Ord a => Signal a -> Signal a -> Signal Bool
- (&&@) :: Signal Bool -> Signal Bool -> Signal Bool
- (||@) :: Signal Bool -> Signal Bool -> Signal Bool
- signalDebug :: Show a => a -> SignalMonad ()
Documentation
A signal is conceptually a time-varying value.
Functor Signal | |
Applicative Signal | The |
Bounded t => Bounded (Signal t) | |
Enum t => Enum (Signal t) | |
Eq (Signal a) | The equality test checks whether two signals are physically the same. |
Floating t => Floating (Signal t) | |
Fractional t => Fractional (Signal t) | |
Integral t => Integral (Signal t) | |
Num t => Num (Signal t) | |
Ord t => Ord (Signal t) | |
Real t => Real (Signal t) | |
Show (Signal a) | The |
data SignalMonad a Source
A restricted monad to create stateful signals in.
createSignal :: SignalMonad a -> IO aSource
:: Signal a | the top-level signal |
-> DTime | the amount of time to advance |
-> IO a | the current value of the signal |
Advancing the whole network that the given signal depends on by the amount of time given in the second argument.
A signal that can be directly fed through the sink function returned. This can be used to attach the network to the outer world.
:: a | initial state |
-> (DTime -> a -> a) | state transformation |
-> SignalMonad (Signal a) |
A pure stateful signal. The initial state is the first output.
:: a | initial internal state |
-> (DTime -> t -> a -> a) | state updater function |
-> Signal t | input signal |
-> SignalMonad (Signal a) |
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 only be directly
observed by the sampleDelayed
function.
:: a | initial output |
-> Signal a | the signal to delay |
-> SignalMonad (Signal a) |
The delay
transfer function emits the value of a signal from the
previous superstep, starting with the filler value given in the first
argument. It has to be a primitive, otherwise it could not be used to
prevent automatic delays.
A continuous sampler that flattens a higher-order signal by outputting its current snapshots.
:: Signal Bool | control (trigger) signal |
-> Signal (SignalMonad a) | a stream of monads to potentially run |
-> Signal (Maybe a) |
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.
:: a | Initial output |
-> Signal (Maybe a) | Maybe signal to latch on |
-> SignalMonad (Signal a) |
toMaybe :: Bool -> a -> Maybe aSource
A helper function to wrap any value in a Maybe
depending on a
boolean condition.
edge :: Signal Bool -> SignalMonad (Signal Bool)Source
The edge
transfer function takes a bool signal and emits another
bool signal that turns true only at the moment when there is a rising
edge on the input.
:: Signal a | the actual output |
-> Signal t | a signal guaranteed to age when this one is sampled |
-> Signal a |
Dependency injection to allow aging signals whose output is not
necessarily needed to produce the current sample of the first
argument. It's equivalent to (flip . liftA2 . flip) const
, as it
evaluates its second argument first.
signalDebug :: Show a => a -> SignalMonad ()Source
A printing function that can be used in the SignalMonad
.
Provided for debugging purposes.