Introducing Reactive: Events
Reactive is Conal Elliott’s newest FRP (functional reactive programming) framework. In this series of blog posts, I’m going to introduce a set of examples by concept. These articles are intended to serve as tutorials and motivation for writing interactive programs using Reactive instead of the IO monad.
A basic knowledge of Haskell and the prelude functions, especially higher order functions like id
and const
, is all that is required.
We’ll start our journey by taking a look at events. Semantically, Events can be thought of as lists of time/value pairs where the times are nondecreasing:
Some varied examples:

[(1.0, 'h'), (2.0, 'e'), (3.0, 'l'), (3.1,'l'), (4.0,'o')]
could be a keyboard event (Event Char
) where the user typed “hello” pressing the keys at those times.

[(50.0,FSharp), (50.0,ASharp), (50.0,CSharp)]
could refer to three notes being plucked on a guitar at time 50.

 [(, ()), (0, ()), (5, ())] is an event that has an occurrence that happens before time. Note that events stretch time to include values at and . This is what the carot (^) signifies in the definition of above.

[ (1,()), (0,()) ]
would not be an event since the times are decreasing.
Although we think of Events as lists, they are not implemented directly as them. Also, Events are abstract in that the representation itself isn’t manipulated directly. We’ll be using many Event functions that also apply to lists, and some that are specific to Events.
Some simple examples
Our examples are going to model various machines that have one button and a speaker that emits one sound. The button event is going to be of type Event ()
. We’re using the () type since these events don’t carry any extra information, unlike a keypress event. Our sound event is going to be similarly represented as Event ()
. So, quite simply our machines are all going to have the following type:
type BellMachine = Event () > Event ()
For our first example, lets implement a simple machine that emits the bell event whenever the button event occurs.
doorBell :: BellMachine doorBell = id
Using reactive, we can implement machines much more complex than a doorbell while retaining the same highlevel of simplicity demonstrated in the above code.
Consider another simple machine. This one sounds a bell 3 minutes after it is turned on. Before we implement it, lets take a look at what functions Reactive includes for generating new events:
 Converts a list of time/value pairs into an event. Note that the times _must_ be nondecreasing. listE :: [(TimeT,a)] > Event a atTimes :: [TimeT] > Event () atTime :: TimeT > Event ()
Assuming that time 0 is when the machine turns on, the bell event could be implemented like this:
 Convert minutes to seconds mToS :: Double > Double mToS = (*) 60  The appropriate time to cook a softboiled egg is 3 minutes (see wikipedia) eggTimer :: Event () eggTimer = atTime (mToS 3)
Note that eggTimer
isn’t a machine of our initial type; It lacks any mention of an attached button. We make it into a machine of the appropriate type by ignoring all button presses using const
.
eggTimerM :: BellMachine eggTimerM = const eggTimer  Alternative Definition  eggTimerM = pure eggTimer
mempty/mappend (Monoid)
Lets look at a machine that sounds the bell at 10 seconds and when the user presses the button. Reflect, for a moment, on how this would be implemented imperatively. Timers? Signals? Event Loops? Compare that sketch to the simplicity of, and codereuse within, the following reactive definition.
nifty :: BellMachine nifty button = eggTimer `mappend` button  alternate definition  nifty = mappend eggTimer
We can think of mappend
as merging events. Note that this function is part of the monoid concept and that mempty
, mappend
’s counterpart, represents an event that never occurs. Taking this into consideration, we can implement a completely silent machine like this:
silent :: BellMachine silent = const mempty  Alternative Definition  silent = pure mempty
(Note: To use mappend and mempty, you’ll need to import Data.Monoid)
A more difficult example (fmap/switchE) (Functor/Monad)
Now lets implement a tricky machine. When the user presses the button twice, the machine begins pulsing the bell with a period equivalent to the time between the button presses. Another press of the button will stop the ringing at which point the process may be repeated.
Lets try to break the implementation into pieces. We know that we’ll need to calculate the difference of time between two event occurrences. For this subproblem we’ll combine two of reactive’s functions, the first being withTimeE
 Combine an event instance's data with its corresponding time. withTimeE :: Event a > Event (a, TimeT)
withTimeE
is typical of reactive’s builtin functions in that it carries along the input Event’s data in the first element of the resultant tuple, while the computed value gets inserted into the second element of the tuple. If we instead wanted to ignore the initial event’s data, we could define a new function, withTimeE_
, as follows:
withTimeE_ :: Event a > Event TimeT withTimeE_ e = fmap snd (withTimeE e)  Alternative definition  withTimeE_ = (fmap.fmap) snd withTimeE
fmap
allows us to apply a function to all the values of an event. fmap
, like mappend/mempty
, works similarly on other data types including functions (see the Functor class defined in Control.Monad).
The function we’ll be using in conjunction with withTimeE
is withPrevE
. withPrevE
couples each value in an event with the previous occurrence’s value.
withPrevE :: Event a > Event (a, a)
Similar to withTimeE
, it adds the computed value (in this case, the previous value) to the snd
part of the tuple.
Combining these two functions, we can transform an Event ()
into an Event TimeT
that represents the time passed since the previous event.
elapsedFirstTry :: Event () > Event TimeT elapsedFirstTry e = fmap f (withPrevE (withTimeE_ e)) where f (tCur, tPrev) = tCur  tPrev  alt. def.  elapsedFirstTry = fmap (uncurry ()) . withPrevE . withTimeE_
The above definition is all right, but in the spirit of reactive’s composability, we’ll define a more generally useful version that carries along the input event’s data and one that ignores it.
withElapsed :: Event a > Event (a, TimeT) withElapsed = fmap f . withPrevE . withTimeE where f ((aCur, tCur), (aPrev, tPrev)) = (aCur, tCurtPrev) withElapsed_ :: Event a > Event TimeT withElapsed_ = (fmap.fmap) snd withElapsed
withElapsed
gets is pretty far along the way of implementing the proposed machine, but we still have a ways to go. Observe that we only care about the elapsed time every 3rd time the button is pressed. To ease identification of these particular presses, we’ll label the event occurrences with a state. First, we’ll define our state data type and a method to cycle them.
data State = Idle  WaitingForPeriodEnd  PlayingCycle  Given a state returns the next in sequence next :: State > State next Idle = WaitingForPeriodEnd next WaitingForPeriodEnd = PlayingCycle next PlayingCycle = Idle
Reactive includes a simple function that will do the Event labeling for us.
  State machine, given initial value and transition function. Carries  along event data. mealy :: s > (s > s) > Event b > Event (b,s) mealy_ :: s > (s > s) > Event b > Event s
Here is what we have so far . . . we’re getting pretty close.
soFar :: Event () > Event (TimeT, State) soFar = mealy Idle next . withElapsed_
How do we generate the pulse events for the PlayingCycle
state? For this type of situation, reactive has a few functions that have the signature Event (Event a) > Event a
. The most general of these is called join which applies to all Monads. In our case we’ll be using the switchE.
  Switch from one event to another, as they occur.
switchE :: Event (Event a) > Event a
To use switchE
we’ll need to fmap our Event (TimeT, State) into an Event (Event ()). But first, lets define a function that will generate our periodic alarms:
period :: TimeT > TimeT > Event ()
period delta t0 = atTimes (iterate ((+) delta) t0)
And now we put it all together:
metronome :: BellMachine
metronome = switchE . fmap f . withTimeE . mealy Idle next . withElapsed_
where
f ((dt,PlayingCycle), t) = period dt t
f _ = mempty
More to come
Our examples using Events are just the tip of the iceberg of reactive’s potential. In the next article, I’ll be explaining reactive’s Behaviors, which can be thought of as functions of time.
Acknowledgments
This tutorial was created by David Sankel and Conal Elliott with important input from Thomas Davie and was sponsored by Anygma
((fmap.fmap) snd withTimeE) seems overly complicated. Certainly was hard to read for someone who’s not used to working on ((>) r). ((fmap snd) . withTimeE) seems much simpler. Same thing with withElapsed_
It would also be nice to mention why you didn’t just use Enum for the State transitions (the answer being because Enum isn’t circular, but it took me a moment to realize that). Of course, if you were to derive Enum, Bounded, and Eq, then you could write a slightly shorter next with something like
next :: State > State next x  x == maxBound = minBound  otherwise = succ x
Of course, this doesn’t seem particularly worthwhile here, but it would be useful if you needed more states. Just a thought.
Sweet article. Reactive is really promising.
I wanted to actually build your machine, so I bugged Conal to add a way to adapt events to IO. Here’s the code I tried.
runMachine :: BellMachine > IO () runMachine machine = do — get button press clock < makeClock (buttonPressed, buttonPressedSink) > sink ())
main = runMachine $ doorBell
This guy works fine for the basic machines but it breaks for metronome. The first reason seems to be that when it gets to a point when there are no future events, switchE fails.
“Exception: Future mempty: it’ll never happen, buddy”
I could hack around this, but then I got to another issue. It seems like switchE is too lazy, which causes my events always fire one click behind when I press the button. My guess is that since switchE is implemented with withRestE, it needs to peak ahead one event.
Not sure how to deal with these issues in practice. Curious if I’m just doing something wrong.
Kevin, thanks for your thoughts on Enum and for pointing out the ((fmap.fmap) snd withTimeE) style. Conal told me that he’ll be blogging about (fmap.fmap…) soon, but I’ll try to illustrate the reasoning behind it here.
Lets say we write a state monad that we’re happy with and now want to compose it with other monads. Unfortunately, we’ll have to rewrite our monad as a monad transformer before we can do that.
This lack of composability does not apply to Functors nor their extension, Applicative Functors. So the (fmap.fmap) format you recognized is how we use the composability of Functors. Check out this bad boy from the reactive source code:
The alternative you suggested works fine in the cases you mention. When we get to Behaviors, and are now working with applicative functors, the (x.x.x) syntax really comes in handy. Lets consider adding two behaviors generated from user input:
And then we factor out the ui part:
Sasha. I also wrote some tests on the bell machines and I’m getting similar results. Unfortunately, reactive is a bit buggy at the moment and has some subtle laziness issues (I suspect it’s due to my implementation of joinE, btw).
I don’t understand the code you posted above and I suspect it may be due to formatting issues. The reactive mailing list is a good place to report the behavior you’re seeing.
Later in this sequence of tutorials I’ll be covering the adaptation of reactive to IO in depth.
hey, I think my formatting got screwed up above, trying agin.
I tried something similar to Sasha, with similar results. I got around the future mempty error using a gigantic hack equivalent to:
fakeEmpty = atTime 9999999
and using this in the place of mEmpty. This “worked”, but I have some other problems… they could be laziness problems in Reactive or they could be my fault. My initial attempt at an IO adapter is a bit more complex than Sasha’s:
adaptEvent :: Sink (Event String > Event (IO ())) adaptEvent f = do { clock < makeClock; (textIn, textInSink) IO () doInput sink = do { l < getLine; sink l; doInput sink; }
This is a very cool piece of software; my mind keeps jumping at possibilities that I don’t have time to implement. It’s great to see something like this being actively developed. I eagerly await a time when this is stable enough to try some more complex things…
Wow, this blog really butchers Haskell in comments…
Please don’t keep us waiting much longer for part two on Behaviors!
For those who are following, the issue with running these examples is in reactive’s trac system here.
Michael, the behavior’s part of the tutorial is going to cover a lot more ground than the above tutorial. It’s coming right along!
It does’t work
$ ghci EventTut.hs GHCi, version 6.10.1: http://www.haskell.org/ghc/ for help Loading package ghcprim … linking … done. Loading package integer … linking … done. Loading package base … linking … done. [1 of 3] Compiling ConsoleAdapter ( ConsoleAdapter.hs, interpreted )
ConsoleAdapter.hs:24:50: Couldn’t match expected type
Event Char' against inferred type
FRP.Reactive.Internal.Misc.Sink a’ In the first argument off', namely
stdinEvent’ In the second argument offmap', namely
(f stdinEvent)’ In the second argument offorkE', namely
(fmap putChar (f stdinEvent))’ Failed, modules loaded: none.Any help would be appreciated
The code is here.
http://hpaste.org/fastcgi/hpaste.fcgi/view?id=2109#a2109
After changing
to
it compiles.
Now to figure out how to use this stuff.
I’m using reactive 0.11 on ghc 6.10.3 I used the above hpaste link to get started. I had to flip the event and the sink like mentioned above, and in adition to that, I had to replace ‘exact’ with ‘exactNB in ConsoleAdapter. It compiles OK, but when trying to run a machine (main = adapt metronome), one thread grabs 100% cpu and memoryuse quickly goes up. nothing happens when I press space.
I would really like to get a bit more familiar with FRP. Any patches to fix this issue?
Thanks
Hey… your function arrows in types are rendered as > Thanks for writing this I’m only a little way in and finding it very interesting.