So far, most of our programs were concerned with simple input to output transformations (e.g. parsing). Naturally, functional programming languages excell at such tasks, as functions are all about transforming input to output.

When we did implement something interactive (the Sokoban game), we resorted taking a very imperative concept, namely a state machine, and implemented it functionally.

Today we will learn about a programming model for tackling inherently interactive tasks, such as user interfaces, robot controlers, computer music and others: Functional reactive programming (FRP). This is a paradigm that allows you to declaratively specify the behavior of a complex interactive system. The principle is independent of the programming language, and you will be able to take some of the concepts and ideas here, and apply them elsewhere.

Classical UI primitives

But before we venture to functional reactive programming, let us have a look at how user interfaces are classically programmed. Here is the code for a typical GUI application, in this case a simple model of a banana dispensing machine:

You set up call backs: Pieces of code to run when certain events happen.

In these callbacks, you read and write status from the widgets, fire up dialoges etc.

A central event loop, provided by the UI toolkit, is started (here start) which will now run forever, waiting for user input and other events, and then running the appropriate callback.

This works, but this is obvioulsy error prone, because we are not declaring our intent, but rather have to implement it. For example, the intention is: „A banana can only be bought when there are at least 10 coins inserted.“ Already we applied this logic at two spots in the program: In the initialization code we disable the buy button because we initialize the counter with 5. And when we add coins, we check if we can enable the button. But we forgot that chek in the callback of the buy button.

And this repeats itself: For every event we have to think of all the possible effecs on the state of the program this can have. Now assume the price of the banana would vary with every banana bought, or could be set separately, or might even depend on some varying factor such as the time of day – this would really complicate things!

Reactive-bananas

One solution to this problem is functional reactive programming, where the interaction between the components is declared once, and the system takes care of doing the right thing.

There are several implementations of FRP around, some more experimental, some more educational and some more practical. I will use reactive-banana, because it has good documentation and explains the concepts quite clearly. I will later comment on an interesting alternative.

Behaviors

The key to reactive programming is to zoom out. Instead of looking at a single piece of a complex system at a given time, and deciding what to next, the idea is too zoom out and look at the behavior over time. Wholemeal programming again!

So one type of interest is that of a Behavior. The real implementation is different, but we can think of it as having the following type:

typeBehavior a =Time-> a

This describes a value over time that is always available. The position of the mouse is a good example of this, or the current time, or some measurement, or the status of a network connection.

This and the following pictures were taken from the reactive-banana package and were created by Heinrich Apfelmus.

We have seen this when working with animations in CodeWorld. The new things now is that we are going to be combinating multiple behaviors. What combinator do we want? We want to

be able to apply a pure function to a behavior,

create a constant behavior and

apply a pure function to more than one behavior.

Clearly, we want to have instances Functor Behavior and Applicative Behavior.

What behaviors exist in our example? The current value of the coin cointer might be modelled as

counter ::BehaviorInteger

and the current price of the banana might be

price ::BehaviorInteger

and then we could derive a behavior that, for any moment in time, indicates whether we can buy a banana:

canBuy ::BehaviorBool
canBuy = (>=) <$> counter <*> price

But this only gets us so far, and on their own, behaviors are not very useful. They are more fun in the interplay with events.

Events

Events occur at certain tims, and may carry a value with them. At a specific time, there can be at most one event. Typical examples are mouse clicks, keyboard presses, status changes of the network connection.

The model type for one event source, is the following:

typeEvent a = [(Time,a)]

with the constraints the the Times are strictly monotonically increasing.

So clearly, a possible event is one that never fires:

never ::Event
never = []

No other events are provided by the FRP framework itself. Any such events, e.g. keyboad presses, are provided from the outside, often via callback functions of the UI library used.

It is not hard to define our own events, but there exists glue code that integrates into various UI frameworks. We will see that in a moment, but for now just assume that you can have events like Event () for button clicks, or Event String for key presses.

Events on their own can be combined in various ways:

There is a Functor Event instance so we have

fmap :: (a -> b) ->Event a ->Event b

Two events can be combined using

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

which just merges the events from two sources. They need to have the same type for that. The first argument indicates what to do if both events fire at the same time: Then this function is used to combine them.

A common case is to combine many events that contain functions, so here we have

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

An event stream can be filtered:

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

The value of an event can be accumulated:

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

Ignore the MonadMoment constraint at this point. This function takes an initial value, and an event that produces functions to modify this value, and returns an event that is the accumulation of all the functions that were sent over this event so far.

For example, let groupSizeEntered :: Event Integer be an event that fires whenever a group enters, and indicates the group event size. Then

fires whenever a group enters, but now indicates the total number of people who have entered so far.

Combining Events and Behaviors

Then there are ways to create events that also involves reading from an behavior:

An extension of fmap allows the function to be varying over time:

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

This function is also available as <@>. Obviously, at the time an event arrives the current value of the behavior is used to modify the value of the event.

A derived notion is

(<@) ::Behavior b ->Event a ->Event b

which fires whenver the given event fires, but takes the value from the behavior.

Note the similarity to the type signatures of <*> and <*; these combinators can be used similarly. Events do not support <*>, i.e. they do not have an applicative instance. They cannot, because two given events do usually not provide a value at the same time.

Similarly, filterE is generalized to

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

Of this, a special case is

whenE ::BehaviorBool->Event a ->Event a

And then there is a way of treating an event as a behavior:

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

Again ignore the constraint for now. This takes a starting value and an event of the same type, and as a behavior, always takes the last seen value of the event:

At just the time of an event, the stepper still has the previous value. This is important in order to have cyclic dependencies between events and behaviors without crashing the program.

Often one would combine stepper with accumE, so this is conveniently available as

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

The Moment Monad and running an event network

In the type signatures above, we ignored a MonadMoment m constraing a few times. This monad is where we wire up all the pieces and combine the events and behaviours the way we want them to. The MonadMoment type class has two instances: Moment and MomentIO. The second allows events to have an external effect, so in a UI application we, likely need that one.

We leave out the definition of the external eventts (eCoin and eBuyPress) for later.

Recursive Do

This code does not work as such, because bMoney is out of scope in the definition of eCanBuy. Reordering the definitions would not help, because these genuinely are recursive. Usually, when using monads and do-notation, one does not want to have such recursive definitions, but in our case, this is precisely what we need to describe the network.

So we need to reach out for a language extension that allows us to to have recursive definintions in do-notation. We add {-# LANGAUGE RecursiveDo #-} to the top of the file and change do to mdo (from μ-do, where μ is used in math to denote fixed points).

There is much that can be said about the theory and RecursiveDo and its applications. For now we just accept that it allows us to refer to things later in a MonadIO computation.

Compliling and activating a network

So what can we do with this network description? First we have to compile it to an event network using

compile ::MomentIO () ->IOEventNetwork

and then, once we have such an EventNetwork, we can start it using

actuate ::EventNetwork->IO ()

The latter will actually start listening to events. But we have not hooked up our events, so lets do that.

We move the definition of our coinMachine into the coinUI function, so that the widgets that we created are in scope. Then we can use the event0 function from Reactive.Banana.WX to create a banana Event from an UI event.

Other FRP libraries

The reactive-banana library is a very nicely designed implementation of FRP, well documented. If it suits your needs, you can and should use it. It comes with bindings to WX, which allows you to create native applications on Linux, Windows and OS X. There are also bindings to SDL, and it is easy to integrate it into your own event handler loop, e.g. for a command line arguments. Also see the list of examples, which includes games.

From the same author there is also threepenny-gui which uses the web browser as a display. “A program written with Threepenny is essentially a small web server that displays the user interface as a web page to any browser that connects to it.”. It has its own implementation of FRP, similar to that of reactive-banana.

Among the many other FRP implementations out there I’d like to highlight reflex together with reflex-dom. This is developed by a company that makes good money by rapidly creating complex browser-based applications, and is based on a very performant FRP applications. It is all written in Haskell, but compiled to JavaScript and runs in the browser. If you want to write highly interactive, slick and complex web applications, this might be the tool of your choice. Unfortunately, documentation is still scarce.