Reactive-banana and the essence of FRP

Slowly but steadily, my custom drawing program for creating teaching videos about Haskell takes shape. Today, I am spinning off my corresponding FRP (functional reactive programming) library and I am releasing it on hackage. Get it here:

Note that this is an experimental first release. But the design is very conservative, which means that it works and that you can use it now to incorporate functional reactive programming in your code.

The main selling point of reactive-banana is that you can freely mix it with existing imperative code and GUI frameworks. In particular, you can hook it into wxHaskell or Gtk2Hs with just a few lines of code. Also, if you get stuck with my library, be it because you don’t understand it well enough or because my library is not expressive enough, you can temporarily switch back to the imperative style and still get things done. Of course, if you have a use case that cannot be expressed with reactive-banana, please let me know so I can extend the design!

At this point, I should probably explain the design philosophy that makes it all work. What led me to my library is the following question:

What is the essence of functional reactive programming?

A common answer would be that “FRP is all about describing a system in terms of time-varying functions instead of mutable state”, and that would certainly not be wrong. This is the semantic viewpoint. But in my opinion, the deeper, more satisfying answer is given by the following purely syntactic criterion:

The essence of functional reactive programming is to specify the dynamic behavior of a value completely at the time of declaration.

For instance, take the example of a counter: you have two buttons labelled “Up” and “Down” which can be used to increment or decrement the counter. Imperatively, you would first specify an initial value and then change it whenever a button is pressed; something like this:

counter := 0                               -- initial value
on buttonUp   = (counter := counter + 1)   -- change it later
on buttonDown = (counter := counter - 1)

The point is that at the time of declaration, only the initial value for the counter is specified; the dynamic behavior of counter is implicit in the rest of the program text. In contrast, functional reactive programming specifies the whole dynamic behavior at the time of declaration, like this:

counter :: Behavior Int
counter = accumulate ($) 0
            (fmap (+1) eventUp
             `union` fmap (subtract 1) eventDown)

Whenever you want to understand the dynamics of counter, you only have to look at its definition. Everything that can happen to it will appear on the right-hand side. This is very much in contrast to the imperative approach where subsequent declarations can change the dynamic behavior of previously declared values.

Of course, to express any interesting dynamic behavior, it is necessary to enrich the semantics and introduce time-varying values. But this is all a consequence of the syntactic desire. Proponents of functional programming will assert that this syntactic essence makes writing correct programs much easier.

Comments

Some HTML formatting is allowed.