An introduction to Function Reactive Programming


Before we start, this quick below chat can give a little taste of what FRP is. So let's look at this first.




Fundamentally, functional reactive programming (FRP) is programming declaratively with time-varying values. The idea is to model things like user input and animations in a more direct, declarative way by making their behavior over time more explicit. In a normal imperative program for something like a GUI, you model change over time implicitly in two ways: using mutable state and using callbacks. FRP models the same notions by introducing two data types representing values that change over time, events and behaviors:
  • events represent a value at a particular time. For example, a keypress would be represented as an Event Char. Pressing the a key at time t would be represented as the pair (t, 'a'). In practice, we never program directly with events; instead, we deal with potentially infinite streams of events ordered by time. So a keyboard input would be a stream of Char events. You can think of streams as representing a value which only exists sometimes: the typed character only exists when the key is struck.
  • behaviors represent values that vary continually over time. These are values that always exist but can be constantly changing. The mouse position is a perfect example: it would be a Behavior Point. The mouse always has a position, but when it's moving that position is constantly changing.
Conceptually, you can think of event streams as infinite lists of time value pairs and behaviors as functions from a time to a value. However, time is not usually explicit; instead, it is managed by the FRP runtime system. This means that your entire program is made by combining behaviors and events in different ways--you never ask for the value of something now but instead define how it behaves based on other time-varying values.

The advantage of this sort of programming is that it abstracts over managing values that change with time. It makes dependencies between different values more explicit and allows you for some very nice combinators. This makes it fairly easy to express your logic at a higher level instead of dealing with the implementation details of mutable state and callbacks.

A good combinator for illustrating the declarative nature of FRP is when. The idea is simple: given a Behavior Bool--a continuouisly varying boolean--we can use it to filter a stream of events. Here's an example adapted from a very simple game of life applet I wrote:
  1. when (not <$> paused) (step <$ time)
The idea here is very simple. paused is a behavior that tells you whether the game is paused or not. time is a stream of events from a timer with one event every n seconds. step <$ time just creates a stream of functions that advance the simulation by one turn every n seconds. The entire expression creates a stream of step functions every seconds as long as the game isn't paused.
I like this particular example because it's simple and easy to relate to. Most of the other combinators are in a similar vein.

Your entire program in FRP ends up being a combination of events and behaviors like this--you start with some primitive ones like mouse actions and combine them to create your own. Finally, you just plug the events/behaviors into outpus as appropriate. For example, if you have a Behavior String, you can just plug that into a label and have the label update to the correct value automatically.
While FRP is probably most often used for GUIs, it can also be used for things like games, synthesizing music or even controllers for robots. It's a very nice technique for specifying reactive systems in general, which are systems that can continually respond to inputs.