reactive-banana- Small but solid library for functional reactive programming (FRP).




Why a third type Discrete?

In an ideal world, users of functional reactive programming would only need to use the notions of Behavior and Event, the first corresponding to value that vary in time and the second corresponding to a stream of event ocurrences.

However, there is the problem of incremental updates. Ideally, users would describe, say, the value of a GUI text field as a Behavior and the reactive-banana implementation would figure out how to map this onto the screen without needless redrawing. In other words, the screen should only be updated when the behavior changes.

While this would be easy to implement in simple cases, it may not always suit the user; there are many different ways of implementing incremental computations. But I don't know a unified theory for them, so I have decided that the reactive-banana will give explicit control over updates to the user in the form of specialized data types like Discrete, and shall not attempt to bake experimental optimizations into the Behavior type.

To sum it up:

  • You get explicit control over updates (the changes function),
  • but you need to learn a third data type Discrete, which almost duplicates the Behavior type.
  • Even though the type Behavior is more fundamental, you will probably use Discrete more often.

That said, Discrete is not a new primitive type, but built from exising types and combinators; you are encouraged to look at the source code.

If you are an FRP implementor, I encourage you to find a better solution. But if you are a user, you may want to accept the trade-off for now.

Discrete time-varying values

data Discrete f a Source

Like Behavior, the type Discrete denotes a value that varies in time. However, unlike Behavior, it also provides a stream of events that indicate when the value has changed. In other words, we can now observe updates.


FRP f => Functor (Discrete f)

Functor instance

FRP f => Applicative (Discrete f)

Applicative instance

FRP f => Apply (Discrete f) (Event f)

Overloading applyD

initial :: Discrete f a -> aSource

Initial value.

changes :: Discrete f a -> Event f aSource

Event that records when the value changes. Simultaneous events may be pruned for efficiency reasons.

value :: Discrete f a -> Behavior f aSource

Behavior corresponding to the value. It is always true that

 value x = stepper (initial x) (changes x)

stepperD :: FRP f => a -> Event f a -> Discrete f aSource

Construct a discrete time-varying value from an initial value and a stream of new values.

accumD :: FRP f => a -> Event f (a -> a) -> Discrete f aSource

Accumulate a stream of events into a discrete time-varying value.

applyD :: FRP f => Discrete f (a -> b) -> Event f a -> Event f bSource

Apply a discrete time-varying value to a stream of events.

 applyD = apply . value