reactive-banana-1.3.2.0: Library for functional reactive programming (FRP).

Safe Haskell | None
Language | Haskell98

Reactive.Banana.Combinators

Contents

- Synopsis
- Core Combinators
  - Event and Behavior
  - First-order
  - Moment and accumulation
  - Recursion
  - Higher-order
- Derived Combinators
  - Infix operators
  - Filtering
  - Accumulation
  - Merging events

Synopsis

- data Event a
- data Behavior a
- interpret :: (Event a -> Moment (Event b)) -> [Maybe a] -> IO [Maybe b]
- module Control.Applicative
- module Data.Semigroup
- never :: Event a
- unionWith :: (a -> a -> a) -> Event a -> Event a -> Event a
- filterE :: (a -> Bool) -> Event a -> Event a
- apply :: Behavior (a -> b) -> Event a -> Event b
- data Moment a
- class MonadFix m => MonadMoment (m :: Type -> Type) where
  - liftMoment :: Moment a -> m a
- accumE :: MonadMoment m => a -> Event (a -> a) -> m (Event a)
- stepper :: MonadMoment m => a -> Event a -> m (Behavior a)
- valueB :: MonadMoment m => Behavior a -> m a
- valueBLater :: MonadMoment m => Behavior a -> m a
- observeE :: Event (Moment a) -> Event a
- switchE :: MonadMoment m => Event a -> Event (Event a) -> m (Event a)
- switchB :: MonadMoment m => Behavior a -> Event (Behavior a) -> m (Behavior a)
- (<@>) :: Behavior (a -> b) -> Event a -> Event b
- (<@) :: Behavior b -> Event a -> Event b
- (@>) :: Event a -> Behavior b -> Event b
- filterJust :: Event (Maybe a) -> Event a
- filterApply :: Behavior (a -> Bool) -> Event a -> Event a
- whenE :: Behavior Bool -> Event a -> Event a
- split :: Event (Either a b) -> (Event a, Event b)
- once :: MonadMoment m => Event a -> m (Event a)
- unions :: [Event (a -> a)] -> Event (a -> a)
- accumB :: MonadMoment m => a -> Event (a -> a) -> m (Behavior a)
- mapAccum :: MonadMoment m => acc -> Event (acc -> (x, acc)) -> m (Event x, Behavior acc)
- merge :: Event a -> Event b -> Event (These a b)
- mergeWith :: (a -> c) -> (b -> c) -> (a -> b -> c) -> Event a -> Event b -> Event c

# Synopsis

The main types and combinators of Functional Reactive Programming (FRP).

At its core, FRP is about two data types `Event` and `Behavior` and the various ways to combine them. There is also a third type `Moment`, which is necessary for the higher-order combinators.

# Core Combinators

## Event and Behavior

data Event a Source #

`Event a` represents a stream of events as they occur in time. Semantically, you can think of `Event a` as an infinite list of values that are tagged with their corresponding time of occurrence,

    type Event a = [(Time,a)]

Each pair is called an event occurrence. Note that within a single event stream, no two event occurrences may happen at the same time.

#### Instances

Instances details

Functor Event Source #

The function `fmap` applies a function `f` to every value. Semantically,

    fmap :: (a -> b) -> Event a -> Event b
    fmap f e = [(time, f a) | (time, a) <- e]

Instance details Defined in Reactive.Banana.Types Methods fmap :: (a -> b) -> Event a -> Event b Source # (<$) :: a -> Event b -> Event a Source #

Semigroup a => Monoid (Event a) Source #

The combinator `mempty` represents an event that never occurs. It is a synonym,

    mempty :: Event a
    mempty = never

Instance details Defined in Reactive.Banana.Types Methods mempty :: Event a Source # mappend :: Event a -> Event a -> Event a Source # mconcat :: [Event a] -> Event a Source #

Semigroup a => Semigroup (Event a) Source #

The combinator `<>` merges two event streams of the same type. In case of simultaneous occurrences, the events are combined with the underlying `Semigroup` operation. Semantically,

    (<>) :: Event a -> Event a -> Event a
    (<>) ex ey = unionWith (<>) ex ey

Instance details Defined in Reactive.Banana.Types Methods (<>) :: Event a -> Event a -> Event a Source # sconcat :: NonEmpty (Event a) -> Event a Source # stimes :: Integral b => b -> Event a -> Event a Source #

data Behavior a Source #

`Behavior a` represents a value that varies in time. Semantically, you can think of it as a function

    type Behavior a = Time -> a

#### Instances

Instances details

Applicative Behavior Source #

The function `pure` returns a value that is constant in time. Semantically,

    pure     :: a -> Behavior a
    pure x    = \time -> x

The combinator `<*>` applies a time-varying function to a time-varying value.

    (<*>)    :: Behavior (a -> b) -> Behavior a -> Behavior b
    fx <*> bx = \time -> fx time $ bx time

Instance details Defined in Reactive.Banana.Types Methods pure :: a -> Behavior a Source # (<*>) :: Behavior (a -> b) -> Behavior a -> Behavior b Source # liftA2 :: (a -> b -> c) -> Behavior a -> Behavior b -> Behavior c Source # (*>) :: Behavior a -> Behavior b -> Behavior b Source # (<*) :: Behavior a -> Behavior b -> Behavior a Source #

Functor Behavior Source #

The function `fmap` applies a function `f` at every point in time. Semantically,

    fmap :: (a -> b) -> Behavior a -> Behavior b
    fmap f b = \time -> f (b time)

Instance details Defined in Reactive.Banana.Types Methods fmap :: (a -> b) -> Behavior a -> Behavior b Source # (<$) :: a -> Behavior b -> Behavior a Source #
(Semigroup a, Monoid a) => Monoid (Behavior a) Source # |
Instance details Defined in Reactive.Banana.Types Methods mempty :: Behavior a Source # mappend :: Behavior a -> Behavior a -> Behavior a Source # mconcat :: [Behavior a] -> Behavior a Source #
Semigroup a => Semigroup (Behavior a) Source # |
Instance details Defined in Reactive.Banana.Types Methods (<>) :: Behavior a -> Behavior a -> Behavior a Source # sconcat :: NonEmpty (Behavior a) -> Behavior a Source # stimes :: Integral b => b -> Behavior a -> Behavior a Source #
IsString a => IsString (Behavior a) Source # |
Instance details Defined in Reactive.Banana.Types Methods fromString :: String -> Behavior a Source #
Floating a => Floating (Behavior a) Source # |

Instance details

Defined in Reactive.Banana.Types

Methods

pi :: Behavior a Source #

exp :: Behavior a -> Behavior a Source #

log :: Behavior a -> Behavior a Source #

sqrt :: Behavior a -> Behavior a Source #

(**) :: Behavior a -> Behavior a -> Behavior a Source #

logBase :: Behavior a -> Behavior a -> Behavior a Source #

sin :: Behavior a -> Behavior a Source #

cos :: Behavior a -> Behavior a Source #

tan :: Behavior a -> Behavior a Source #

asin :: Behavior a -> Behavior a Source #

acos :: Behavior a -> Behavior a Source #

atan :: Behavior a -> Behavior a Source #

sinh :: Behavior a -> Behavior a Source #

cosh :: Behavior a -> Behavior a Source #

tanh :: Behavior a -> Behavior a Source #

asinh :: Behavior a -> Behavior a Source #

acosh :: Behavior a -> Behavior a Source #

atanh :: Behavior a -> Behavior a Source #

log1p :: Behavior a -> Behavior a Source #

expm1 :: Behavior a -> Behavior a Source #

log1pexp :: Behavior a -> Behavior a Source #

log1mexp :: Behavior a -> Behavior a Source #

Num a => Num (Behavior a) Source # |
Instance details Defined in Reactive.Banana.Types Methods (+) :: Behavior a -> Behavior a -> Behavior a Source # (-) :: Behavior a -> Behavior a -> Behavior a Source # (*) :: Behavior a -> Behavior a -> Behavior a Source # negate :: Behavior a -> Behavior a Source # abs :: Behavior a -> Behavior a Source # signum :: Behavior a -> Behavior a Source # fromInteger :: Integer -> Behavior a Source #
Fractional a => Fractional (Behavior a) Source # |
Instance details Defined in Reactive.Banana.Types Methods (/) :: Behavior a -> Behavior a -> Behavior a Source # recip :: Behavior a -> Behavior a Source # fromRational :: Rational -> Behavior a Source #

interpret :: (Event a -> Moment (Event b)) -> [Maybe a] -> IO [Maybe b] Source #

Interpret an event processing function. Useful for testing.

Note: You can safely assume that this function is pure, even though the type seems to suggest otherwise. I'm really sorry about the extra `IO`, but it can't be helped. See source code for the sordid details.

## First-order

This subsections lists the primitive first-order combinators for FRP. The `Functor`, `Applicative` and `Monoid` instances are also part of this, but they are documented at the types `Event` and `Behavior`.

module Control.Applicative

module Data.Semigroup

never :: Event a Source #

Event that never occurs. Semantically,

    never = []

unionWith :: (a -> a -> a) -> Event a -> Event a -> Event a Source #

Merge two event streams of the same type. The function argument specifies how event values are to be combined in case of a simultaneous occurrence. The semantics are

    unionWith f ((timex,x):xs) ((timey,y):ys)
       | timex <  timey = (timex,x)     : unionWith f xs ((timey,y):ys)
       | timex >  timey = (timey,y)     : unionWith f ((timex,x):xs) ys
       | timex == timey = (timex,f x y) : unionWith f xs ys

filterE :: (a -> Bool) -> Event a -> Event a Source #

Allow all events that fulfill the predicate, discard the rest. Semantically,

    filterE p es = [(time,a) | (time,a) <- es, p a]

apply :: Behavior (a -> b) -> Event a -> Event b Source #

Apply a time-varying function to a stream of events. Semantically,

    apply bf ex = [(time, bf time x) | (time, x) <- ex]

This function is generally used in its infix variant `<@>`.

## Moment and accumulation

data Moment a Source #

The `Moment` monad denotes a pure computation that happens at one particular moment in time. Semantically, it is a reader monad

    type Moment a = Time -> a

When run, the argument tells the time at which this computation happens.

Note that in this context, time really means to logical time. Of course, every calculation on a computer takes some amount of wall-clock time to complete. Instead, what is meant here is the time as it relates to `Event`s and `Behavior`s. We use the fiction that every calculation within the `Moment` monad takes zero logical time to perform.

#### Instances

Instances details

Applicative Moment Source # |
Instance details Defined in Reactive.Banana.Types Methods pure :: a -> Moment a Source # (<*>) :: Moment (a -> b) -> Moment a -> Moment b Source # liftA2 :: (a -> b -> c) -> Moment a -> Moment b -> Moment c Source # (*>) :: Moment a -> Moment b -> Moment b Source # (<*) :: Moment a -> Moment b -> Moment a Source #
Functor Moment Source # |
Instance details Defined in Reactive.Banana.Types Methods fmap :: (a -> b) -> Moment a -> Moment b Source # (<$) :: a -> Moment b -> Moment a Source #
Monad Moment Source # |
Instance details Defined in Reactive.Banana.Types Methods (>>=) :: Moment a -> (a -> Moment b) -> Moment b Source # (>>) :: Moment a -> Moment b -> Moment b Source # return :: a -> Moment a Source #
MonadFix Moment Source # |
Instance details Defined in Reactive.Banana.Types Methods mfix :: (a -> Moment a) -> Moment a Source #
MonadMoment Moment Source # |
Instance details Defined in Reactive.Banana.Types Methods liftMoment :: Moment a -> Moment a Source #
Monoid a => Monoid (Moment a) Source # |
Instance details Defined in Reactive.Banana.Types Methods mempty :: Moment a Source # mappend :: Moment a -> Moment a -> Moment a Source # mconcat :: [Moment a] -> Moment a Source #
Semigroup a => Semigroup (Moment a) Source # |
Instance details Defined in Reactive.Banana.Types Methods (<>) :: Moment a -> Moment a -> Moment a Source # sconcat :: NonEmpty (Moment a) -> Moment a Source # stimes :: Integral b => b -> Moment a -> Moment a Source #

class MonadFix m => MonadMoment (m :: Type -> Type) where Source #

An instance of the `MonadMoment` class denotes a computation that happens at one particular moment in time. Unlike the `Moment` monad, it need not be pure anymore.

Methods

liftMoment :: Moment a -> m a Source #

#### Instances

Instances details

MonadMoment Moment Source # |
Instance details Defined in Reactive.Banana.Types Methods liftMoment :: Moment a -> Moment a Source #
MonadMoment MomentIO Source # |
Instance details Defined in Reactive.Banana.Types Methods liftMoment :: Moment a -> MomentIO a Source #
MonadMoment m => MonadMoment (MaybeT m) Source # |
Instance details Defined in Reactive.Banana.Types Methods liftMoment :: Moment a -> MaybeT m a Source #
(MonadMoment m, Monoid w) => MonadMoment (AccumT w m) Source # |
Instance details Defined in Reactive.Banana.Types Methods liftMoment :: Moment a -> AccumT w m a Source #
MonadMoment m => MonadMoment (ExceptT e m) Source # |
Instance details Defined in Reactive.Banana.Types Methods liftMoment :: Moment a -> ExceptT e m a Source #
MonadMoment m => MonadMoment (IdentityT m) Source # |
Instance details Defined in Reactive.Banana.Types Methods liftMoment :: Moment a -> IdentityT m a Source #
MonadMoment m => MonadMoment (ReaderT r m) Source # |
Instance details Defined in Reactive.Banana.Types Methods liftMoment :: Moment a -> ReaderT r m a Source #
MonadMoment m => MonadMoment (StateT s m) Source # |
Instance details Defined in Reactive.Banana.Types Methods liftMoment :: Moment a -> StateT s m a Source #
MonadMoment m => MonadMoment (StateT s m) Source # |
Instance details Defined in Reactive.Banana.Types Methods liftMoment :: Moment a -> StateT s m a Source #
MonadMoment m => MonadMoment (WriterT w m) Source # |
Instance details Defined in Reactive.Banana.Types Methods liftMoment :: Moment a -> WriterT w m a Source #
(MonadMoment m, Monoid w) => MonadMoment (WriterT w m) Source # |
Instance details Defined in Reactive.Banana.Types Methods liftMoment :: Moment a -> WriterT w m a Source #
(MonadMoment m, Monoid w) => MonadMoment (WriterT w m) Source # |
Instance details Defined in Reactive.Banana.Types Methods liftMoment :: Moment a -> WriterT w m a Source #
MonadMoment m => MonadMoment (RWST r w s m) Source # |
Instance details Defined in Reactive.Banana.Types Methods liftMoment :: Moment a -> RWST r w s m a Source #
(MonadMoment m, Monoid w) => MonadMoment (RWST r w s m) Source # |
Instance details Defined in Reactive.Banana.Types Methods liftMoment :: Moment a -> RWST r w s m a Source #
(MonadMoment m, Monoid w) => MonadMoment (RWST r w s m) Source # |
Instance details Defined in Reactive.Banana.Types Methods liftMoment :: Moment a -> RWST r w s m a Source #

accumE :: MonadMoment m => a -> Event (a -> a) -> m (Event a) Source #

The `accumE` function accumulates a stream of event values, similar to a strict left scan, `scanl'`. It starts with an initial value and emits a new value whenever an event occurrence happens. The new value is calculated by applying the function in the event to the old value.

Example:

    accumE "x" [(time1,(++"y")),(time2,(++"z"))]
        = trimE [(time1,"xy"),(time2,"xyz")]
        where
        trimE e start = [(time,x) | (time,x) <- e, start <= time]

stepper :: MonadMoment m => a -> Event a -> m (Behavior a) Source #

Construct a time-varying function from an initial value and a stream of new values. The result will be a step function. Semantically,

    stepper x0 ex = \time1 -> \time2 ->
        last (x0 : [x | (timex,x) <- ex, time1 <= timex, timex < time2])

Here is an illustration of the result Behavior at a particular time:

Note: The smaller-than-sign in the comparison `timex < time2` means that at time `time2 == timex`, the value of the Behavior will still be the previous value. In the illustration, this is indicated by the dots at the end of each step. This allows for recursive definitions. See the discussion below for more on recursion.

## Recursion

Recursion is a very important technique in FRP that is not apparent from the type signatures.

Here is a prototypical example. It shows how the `accumE` can be expressed in terms of the `stepper` and `apply` functions by using recursion:

    accumE a e1 = mdo
       let e2 = (\a f -> f a) <$> b <@> e1
       b <- stepper a e2
       return e2

(The `mdo` notation refers to value recursion in a monad. The `MonadFix` instance for the `Moment` class enables this kind of recursive code.) (Strictly speaking, this also means that `accumE` is not a primitive, because it can be expressed in terms of other combinators.)

This general pattern appears very often in practice: A Behavior (here `b`) controls what value is put into an Event (here `e2`), but at the same time, the Event contributes to changes in this Behavior. Modeling this situation requires recursion.

For another example, consider a vending machine that sells banana juice. The amount that the customer still has to pay for a juice is modeled by a Behavior `bAmount`. Whenever the customer inserts a coin into the machine, an Event `eCoin` occurs, and the amount will be reduced. Whenver the amount goes below zero, an Event `eSold` will occur, indicating the release of a bottle of fresh banana juice, and the amount to be paid will be reset to the original price. The model requires recursion, and can be expressed in code as follows:

    mdo
        let price = 50 :: Int
        bAmount  <- accumB price $ unions
                      [ subtract 10 <$ eCoin
                      , const price <$ eSold ]
        let eSold = whenE ((<= 0) <$> bAmount) eCoin

On one hand, the Behavior `bAmount` controls whether the Event `eSold` occcurs at all; the bottle of banana juice is unavailable to penniless customers. But at the same time, the Event `eSold` will cause a reset of the Behavior `bAmount`, so both depend on each other.

Recursive code like this examples works thanks to the semantics of `stepper`. In general, mutual recursion between several `Event`s and `Behavior`s is always well-defined, as long as an Event depends on itself only via a Behavior, and vice versa.

## Higher-order

valueB :: MonadMoment m => Behavior a -> m a Source #

Obtain the value of the `Behavior` at a given moment in time. Semantically, it corresponds to

    valueB b = \time -> b time

Note: The value is immediately available for pattern matching. Unfortunately, this means that `valueB` is unsuitable for use with value recursion in the `Moment` monad. If you need recursion, please use `valueBLater` instead.

valueBLater :: MonadMoment m => Behavior a -> m a Source #

Obtain the value of the `Behavior` at a given moment in time. Semantically, it corresponds to

    valueBLater b = \time -> b time

Note: To allow for more recursion, the value is returned lazily and not available for pattern matching immediately. It can be used safely with most combinators like `stepper`. If that doesn't work for you, please use `valueB` instead.

observeE :: Event (Moment a) -> Event a Source #

Observe a value at those moments in time where event occurrences happen. Semantically,

    observeE e = [(time, m time) | (time, m) <- e]

switchE :: MonadMoment m => Event a -> Event (Event a) -> m (Event a) Source #

Dynamically switch between `Event`. Semantically,

    switchE e0 ee0 time0 =
        concat [ trim t1 t2 e | (t1,t2,e) <- intervals ee ]
      where
        laterThan e time0  = [(timex,x) | (timex,x) <- e, time0 < timex ]
        ee                 = [(time0, e0)] ++ (ee0 `laterThan` time0)
        intervals ee       = [(time1, time2, e) | ((time1,e),(time2,_)) <- zip ee (tail ee)]
        trim time1 time2 e = [x | (timex,x) <- e, time1 < timex, timex <= time2]

switchB :: MonadMoment m => Behavior a -> Event (Behavior a) -> m (Behavior a) Source #

Dynamically switch between `Behavior`. Semantically,

     switchB b0 eb = \time0 -> \time1 ->
        last (b0 : [b | (timeb,b) <- eb, time0 <= timeb, timeb < time1]) time1

# Derived Combinators

## Infix operators

(<@>) :: Behavior (a -> b) -> Event a -> Event b infixl 4 Source #

Infix synonym for the `apply` combinator. Similar to `<*>`.

    infixl 4 <@>

(<@) :: Behavior b -> Event a -> Event b infixl 4 Source #

Tag all event occurrences with a time-varying value. Similar to `<*`.

    infixl 4 <@

(@>) :: Event a -> Behavior b -> Event b infixl 4 Source #

Tag all event occurences with a time-varying value. Similar to `*>`.

This is the flipped version of `<@`, but can be useful when combined with `ApplicativeDo` to sample from multiple `Behavior`s:

    reactimate $ onEvent @> do
      x <- behavior1
      y <- behavior2
      return (print (x + y))

## Filtering

filterJust :: Event (Maybe a) -> Event a Source #

Allow all event occurrences that are `Just` values, discard the rest. Variant of `filterE`.

filterApply :: Behavior (a -> Bool) -> Event a -> Event a Source #

Allow all events that fulfill the time-varying predicate, discard the rest. Generalization of `filterE`.

whenE :: Behavior Bool -> Event a -> Event a Source #

Allow events only when the behavior is `True`. Variant of `filterApply`.

split :: Event (Either a b) -> (Event a, Event b) Source #

Split event occurrences according to a tag. The `Left` values go into the left component while the `Right` values go into the right component of the result.

once :: MonadMoment m => Event a -> m (Event a) Source #

Keep only the next occurence of an event.

`once` also aids the garbage collector by indicating that the result event can be discarded after its only occurrence.

    once e = \time0 -> take 1 [(t, a) | (t, a) <- e, time0 <= t]

## Accumulation

Note: All accumulation functions are strict in the accumulated value!

Note: The order of arguments is `acc -> (x,acc)` which is also the convention used by `unfoldr` and `State`.

unions :: [Event (a -> a)] -> Event (a -> a) Source #

Merge event streams whose values are functions. In case of simultaneous occurrences, the functions at the beginning of the list are applied after the functions at the end.

    unions [] = never
    unions xs = foldr1 (unionWith (.)) xs

Very useful in conjunction with accumulation functions like `accumB` and `accumE`.

accumB :: MonadMoment m => a -> Event (a -> a) -> m (Behavior a) Source #

The `accumB` function accumulates event occurrences into a `Behavior`.

The value is accumulated using `accumE` and converted into a time-varying value using `stepper`.

Example:

    accumB "x" [(time1,(++"y")),(time2,(++"z"))]
       = stepper "x" [(time1,"xy"),(time2,"xyz")]

Note: As with `stepper`, the value of the behavior changes "slightly after" the events occur. This allows for recursive definitions.

mapAccum :: MonadMoment m => acc -> Event (acc -> (x, acc)) -> m (Event x, Behavior acc) Source #

Efficient combination of `accumE` and `accumB`.

## Merging events

merge :: Event a -> Event b -> Event (These a b) Source #

Merge two event streams of any type.

mergeWith Source #

Arguments

:: (a -> c) | The function called when only the first event emits a value.
-> (b -> c) | The function called when only the second event emits a value.
-> (a -> b -> c) | The function called when both events emit values simultaneously.
-> Event a |
-> Event b |
-> Event c |

Merge two event streams of any type.

This function generalizes `unionWith`.