Safe Haskell | Safe-Infered |
---|

Sodium Reactive Programming (FRP) system.

See the *examples* directory for test cases and examples.

The `p`

type parameter determines the *partition* that your FRP is running
on. A thread is automatically created for each partition used in the system based
on the unique concrete p type, which must be an instance of Typeable. FRP
processing runs on this thread, but `synchronously`

will block the calling thread
while it waits for FRP processing to complete.

In most cases you would just use one concrete partition type for everything, defined like this:

{-# LANGUAGE DeriveDataTypeable, EmptyDataDecls #-} import Data.Typeable data M deriving Typeable

Later, if you want your code to be more parallel, you can add more partitions:
The `cross`

and `crossE`

functions are used to move events and behaviours between
partitions. The separation thus created allows your FRP logic to be partitioned
so that the different partitions can run in parallel, with more relaxed guarantees
of consistency between partitions.

Some functions are pure, and others need to run under the `Reactive`

monad via
`synchronously`

or `asynchronously`

. An `Event`

*p (*`Reactive`

*p a)* can be flattened
to an `Event`

*p a* using the `execute`

primitive.

In addition to the explicit functions in the language, note that you can use

- Functor on
`Event`

and`Behaviour`

- Applicative on
`behaviour`

, e.g.`let bsum = (+) <$> ba <*> bb`

- Applicative
`pure`

is used to give a constant`Behaviour`

. - Recursive do (via DoRec) to make state loops with the
`rec`

keyword.

Here's an example of recursive do to write state-keeping loops. Note that
all `hold`

s are delayed, so `attachWith`

will capture the *old* value of the state *s*.

{-# LANGUAGE DoRec #-} -- | Accumulate on input event, outputting the new state each time. accumE :: Typeable p => (a -> s -> s) -> s -> Event p a -> Reactive p (Event p s) accumE f z ea = do rec let es = attachWith f ea s s <- hold z es return es

- data Reactive p a
- synchronously :: Typeable p => Reactive p a -> IO a
- asynchronously :: Typeable p => Reactive p () -> IO ()
- newEvent :: Typeable p => IO (Event p a, a -> Reactive p ())
- listenIO :: Event p a -> (a -> IO ()) -> Reactive p (IO ())
- listenValueIO :: Behaviour p a -> (a -> IO ()) -> Reactive p (IO ())
- data Event p a
- data Behaviour p a
- type Behavior p a = Behaviour p a
- never :: Event p a
- merge :: Typeable p => Event p a -> Event p a -> Event p a
- mergeWith :: Typeable p => (a -> a -> a) -> Event p a -> Event p a -> Event p a
- justE :: Typeable p => Event p (Maybe a) -> Event p a
- hold :: a -> Event p a -> Reactive p (Behaviour p a)
- valueEvent :: Typeable p => Behaviour p a -> Event p a
- attachWith :: Typeable p => (a -> b -> c) -> Event p a -> Behaviour p b -> Event p c
- switchE :: Typeable p => Behaviour p (Event p a) -> Event p a
- switch :: Typeable p => Behaviour p (Behaviour p a) -> Reactive p (Behaviour p a)
- execute :: Typeable p => Event p (Reactive p a) -> Event p a
- sample :: Behaviour p a -> Reactive p a
- filterE :: Typeable p => (a -> Bool) -> Event p a -> Event p a
- attach :: Typeable p => Event p a -> Behaviour p b -> Event p (a, b)
- tag :: Typeable p => Event p a -> Behaviour p b -> Event p b
- gate :: Typeable p => Event p a -> Behaviour p Bool -> Event p a
- collectE :: Typeable p => (a -> s -> (b, s)) -> s -> Event p a -> Reactive p (Event p b)
- collect :: Typeable p => (a -> s -> (b, s)) -> s -> Behaviour p a -> Reactive p (Behaviour p b)
- accumE :: Typeable p => (a -> s -> s) -> s -> Event p a -> Reactive p (Event p s)
- accum :: Typeable p => (a -> s -> s) -> s -> Event p a -> Reactive p (Behaviour p s)
- countE :: Typeable p => Event p a -> Reactive p (Event p Int)
- count :: Typeable p => Event p a -> Reactive p (Behaviour p Int)
- once :: Typeable p => Event p a -> Reactive p (Event p a)
- crossE :: (Typeable p, Typeable q) => Event p a -> Reactive p (Event q a)
- cross :: (Typeable p, Typeable q) => Behaviour p a -> Reactive p (Behaviour q a)

# Running FRP code

synchronously :: Typeable p => Reactive p a -> IO aSource

newEvent :: Typeable p => IO (Event p a, a -> Reactive p ())Source

Returns an event, and a push action for pushing a value into the event.

listenIO :: Event p a -> (a -> IO ()) -> Reactive p (IO ())Source

Listen for firings of this event. The returned `IO ()`

is an IO action
that unregisters the listener. This is the observer pattern.

listenValueIO :: Behaviour p a -> (a -> IO ()) -> Reactive p (IO ())Source

Listen to the value of this behaviour with a guaranteed initial callback giving the current value, followed by callbacks for any updates.

# FRP core language

A stream of events. The individual firings of events are called 'event occurrences'.

A time-varying value, British spelling.

Functor (Behaviour p) | |

Typeable p => Applicative (Behaviour p) |

merge :: Typeable p => Event p a -> Event p a -> Event p aSource

Merge two streams of events of the same type.

In the case where two event occurrences are simultaneous (i.e. both within the same transaction), both will be delivered in the same transaction.

The order is not defined, because simultaneous events should be considered to be order-agnostic.

mergeWith :: Typeable p => (a -> a -> a) -> Event p a -> Event p a -> Event p aSource

Merge two streams of events of the same type, combining simultaneous event occurrences.

In the case where multiple event occurrences are simultaneous (i.e. all within the same transaction), they are combined using the supplied function. The output event is guaranteed not to have more than one event occurrence per transaction.

The combine function should be commutative, because simultaneous events should be considered to be order-agnostic.

justE :: Typeable p => Event p (Maybe a) -> Event p aSource

Unwrap Just values, and discard event occurrences with Nothing values.

hold :: a -> Event p a -> Reactive p (Behaviour p a)Source

Create a behaviour with the specified initial value, that gets updated by the values coming through the event. The 'current value' of the behaviour is notionally the value as it was 'at the start of the transaction'. That is, state updates caused by event firings get processed at the end of the transaction.

valueEvent :: Typeable p => Behaviour p a -> Event p aSource

An event that fires once for the current value of the behaviour, and then for all changes that occur after that.

attachWith :: Typeable p => (a -> b -> c) -> Event p a -> Behaviour p b -> Event p cSource

Sample the behaviour at the time of the event firing. Note that the 'current value'
of the behaviour that's sampled is the value as at the start of the transaction
before any state changes of the current transaction are applied through `hold`

s.

switchE :: Typeable p => Behaviour p (Event p a) -> Event p aSource

Unwrap an event inside a behaviour to give a time-varying event implementation.

switch :: Typeable p => Behaviour p (Behaviour p a) -> Reactive p (Behaviour p a)Source

Unwrap a behaviour inside another behaviour to give a time-varying behaviour implementation.

execute :: Typeable p => Event p (Reactive p a) -> Event p aSource

Execute the specified `Reactive`

action inside an event.

# Derived FRP functions

filterE :: Typeable p => (a -> Bool) -> Event p a -> Event p aSource

Only keep event occurrences for which the predicate is true.

attach :: Typeable p => Event p a -> Behaviour p b -> Event p (a, b)Source

Variant of attachWith defined as *attachWith (,)*

tag :: Typeable p => Event p a -> Behaviour p b -> Event p bSource

Variant of attachWith that throws away the event's value and captures the behaviour's.

gate :: Typeable p => Event p a -> Behaviour p Bool -> Event p aSource

Let event occurrences through only when the behaviour's value is True. Note that the behaviour's value is as it was at the start of the transaction, that is, no state changes from the current transaction are taken into account.

collectE :: Typeable p => (a -> s -> (b, s)) -> s -> Event p a -> Reactive p (Event p b)Source

Transform an event with a generalized state loop (a mealy machine). The function is passed the input and the old state and returns the new state and output value.

collect :: Typeable p => (a -> s -> (b, s)) -> s -> Behaviour p a -> Reactive p (Behaviour p b)Source

Transform a behaviour with a generalized state loop (a mealy machine). The function is passed the input and the old state and returns the new state and output value.

accumE :: Typeable p => (a -> s -> s) -> s -> Event p a -> Reactive p (Event p s)Source

Accumulate on input event, outputting the new state each time.

accum :: Typeable p => (a -> s -> s) -> s -> Event p a -> Reactive p (Behaviour p s)Source

Accumulate on input event, holding state.

countE :: Typeable p => Event p a -> Reactive p (Event p Int)Source

Count event occurrences, starting with 1 for the first occurrence.

count :: Typeable p => Event p a -> Reactive p (Behaviour p Int)Source

Count event occurrences, giving a behaviour that starts with 0 before the first occurrence.

once :: Typeable p => Event p a -> Reactive p (Event p a)Source

Throw away all event occurrences except for the first one.