- data Event a
- never :: Event a
- fromEventSource :: EventSource a -> Event a
- reactimate :: Event (IO ()) -> Prepare ()
- mapIO :: (a -> IO b) -> Event a -> Event b
- filter :: (a -> Bool) -> Event a -> Event a
- filterChanges :: Event (Change a) -> Event a
- union :: Event a -> Event a -> Event a
- merge :: Event a -> Event b -> Event (Either a b)
- orderedDuplicate :: Event a -> (Event a, Event a)
- traceEvent :: Show a => String -> Event a -> Event a
- data Behavior a
- behavior :: a -> Event a -> Behavior a
- always :: a -> Behavior a
- initial :: Behavior a -> a
- changes :: Behavior a -> Event a
- apply :: Behavior (a -> b) -> Event a -> Event b
- accumulate' :: (b -> a -> a) -> a -> Event b -> Behavior a
- accumulateChange :: (b -> a -> Change a) -> a -> Event b -> Behavior a
- accumulateIO :: (b -> a -> IO a) -> a -> Event b -> Behavior a
- accumulateIOChange :: (b -> a -> IO (Change a)) -> a -> Event b -> Behavior a
- mapAccum :: (acc -> x -> (acc, y)) -> acc -> Event x -> (Behavior acc, Event y)
- data Change a
- isChange :: Change a -> Bool
- isKeep :: Change a -> Bool
- data EventSource a = EventSource {
- setEventHandler :: (a -> IO ()) -> Prepare ()
- getEventHandler :: Prepare (a -> IO ())
- type Prepare a = IO a
- newEventSource :: Prepare (EventSource a)
- fire :: EventSource a -> a -> IO ()
- testCounter :: Prepare (EventSource Int)
- testApply :: Prepare (EventSource Int, EventSource Int)
Events
The Event
type constructor is one of the cornerstones of the present
approach to functional reactive programmings.
It represents a stream of values as they occur in time.
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 occurence,
type Event a = [(Time,a)]
Note that this is a semantic model; the type is not actually implement that way, but you can often treat it as if it where. In particular, most of the subsequent operations will be explained in terms of this model.
The value never
denotes the event that never happens.
We can model it as the empty stream of events, never = []
.
fromEventSource :: EventSource a -> Event aSource
Derive an Event
from an EventSource
.
Apart from never
, this is the only way to construct events.
reactimate :: Event (IO ()) -> Prepare ()Source
Schedule an IO event to be executed whenever it happens. This is the only way to observe events. Semantically, you could write it as something like this
reactimate ((time,action):es) = atTime time action >> reactimate es
The Prepare
monad indicates that you should call this function
during program initialization only.
filter :: (a -> Bool) -> Event a -> Event aSource
Pass all events that fulfill the predicate, discard the rest. Semantically,
filter p es = [(time,a) | (time,a) <- es, p a]
filterChanges :: Event (Change a) -> Event aSource
Unpacks event values of the form Change _
and discards
everything else.
union :: Event a -> Event a -> Event aSource
Merge two event streams of the same type. Semantically, we have
union ((time1,a1):es1) ((time2,a2):es2) | time1 < time2 = (time1,a1) : union es1 ((time2,a2):es2) | time1 > time2 = (time2,a2) : union ((time1,a1):es1) es2 | otherwise = ... -- either of the previous two cases
Note that the order of events that happen simultaneously is undefined.
This is not a problem most of the time,
but sometimes you have to force a certain order.
In that case, you have to combine this with the orderedDuplicate
function.
merge :: Event a -> Event b -> Event (Either a b)Source
Merge two event streams that have differen types. Semantically, we have
merge e1 e2 = fmap Left e1 `union` fmap Right e2
orderedDuplicate :: Event a -> (Event a, Event a)Source
Duplicate an event stream while paying attention to ordering. Events from the first duplicate (and anything derived from them) will always happen before the events from the second duplicate. Use this function to fine-tune the order of events.
traceEvent :: Show a => String -> Event a -> Event aSource
Debugging helper. Prints the first argument and the value of the event
whenever it happens to stderr
.
Behaviors
The Behavior
type constructor is the other cornerstone of the
present approach to functional reactive programming.
It represents a value that changes with time.
Behavior a
represents a value in time. Think of it as
type Behavior a = Time -> a
However, note that this model misses an important point: we only allow piecewise constant functions. Continuous behaviors like
badbehavior = \time -> 2*time
cannot be implemented.
Functor Behavior | The fmap f behavior = \time -> f (behavior time) |
Applicative Behavior | The pure a = always a bf <*> bx = \time -> bf time $ bx time |
behavior :: a -> Event a -> Behavior aSource
Smart constructor. Supply an initial value and a sequence of changes. In particular,
initial (behavior a es) = a changes (behavior a es) = es
changes :: Behavior a -> Event aSource
An event stream recording how the behavior changes Remember that behaviors are piecewise constant functions.
apply :: Behavior (a -> b) -> Event a -> Event bSource
The most important way to combine behaviors and events.
The apply
function applies a time-varying function to a stream of events.
Semantically,
apply bf es = [(time, bf time a) | (time, a) <- es]
(Theoretically inclined people might
be wondering whether we could achieve the same effect with
the Applicative
instance. The answer is no, the semantics of
apply
and <*>
are subtly different. That's why we need to distinguish
between behaviors and events.)
accumulate' :: (b -> a -> a) -> a -> Event b -> Behavior aSource
The most important way to create behaviors.
The accumulate'
function is similar to a strict left fold, foldl'
.
It starts with an initial value and combines it with incoming events.
For example, semantically
accumulate' (++) "x" [(time1,"y"),(time2,"z")] = behavior "x" [(time1,"yx"),(time2,"zyx")]
Note that the accumulated value is evaluated strictly. This prevents space leaks.
It is recommended that you use the accumulate
function from
Reactive.Classes
to pick types automatically.
accumulateChange :: (b -> a -> Change a) -> a -> Event b -> Behavior aSource
accumulateIO :: (b -> a -> IO a) -> a -> Event b -> Behavior aSource
Version of accumulate
that performs an IO
action to update the value.
It is recommended that you use the accumulate
function from
Reactive.Classes
to pick types automatically.
mapAccum :: (acc -> x -> (acc, y)) -> acc -> Event x -> (Behavior acc, Event y)Source
Map events while threading state.
Similar to the standard mapAccumL
function.
The Change
data type
Data type to indicate that a value has changed.
Used in conjunction with the accumulate
functions.
This is basically the Maybe
type with a different name.
Using a different name improves program readability
and makes it easier to automatically select the right accumulate
function by type, see the Reactive.Classes
module.
Event Sources
After having read all about Event
s and Behavior
s,
you want to hook things up to an existing event-based framework,
like wxHaskell
or Gtk2Hs
.
How do you do that?
EventSource
s are a small bookkeeping device that helps you with that.
Basically, they store event handlers. Often, you can just obtain them from
corresponding bookkeeping devices from your framework,
but sometimes you have to create your own EventSource
and use the fire
function to hook it into the framework.
Event sources are also useful for testing.
After creating an EventSource
,
you can finally obtain an Event
via the fromEventSource
function.
data EventSource a Source
An EventSource
is a facility where you can register
callback functions, aka event handlers.
EventSource
s are the precursor of proper Event
s.
EventSource | |
|
newEventSource :: Prepare (EventSource a)Source
Create a new store for callback functions.
They have to be fired manually with the fire
function.
fire :: EventSource a -> a -> IO ()Source
Fire the event handler of an event source manually. Useful for hooking into external event sources.