Reactive programming has become essential for the way we make apps. Most companies today use some sort of a reactive framework, ranging from μFrameworks that implement the most basic Actor model, through hundreds of Future/Promise frameworks with different levels of API richness, all the way up to massive FRP frameworks that introduce a new programming paradigm and shape our application architecture in the process.
When did this change happen? Is my 2012 object oriented MVC app written in Objective-C not reactive? The purpose of an application has always been the same: to present a user interface that reacts to changes in the outside world – user interactions as well as other external events like data changes on the server. In this sense, your apps have always been as reactive as you made them. But chances are they weren’t written reactively. Also, chances are the user experience was lacking as well (props if this is not true). As technologies progress, users come to expect certain things. As we stare at our fresh facebook post, waiting for the likes to come in, we no longer pull on the refresh control every 1.5 seconds, like we used to do only a couple years ago. We know the app will react, whenever the new data comes in.
So reactive programming isn’t merely a different, although nowadays pretty obvious, way to write apps, with emphasis on data flow. The switch from message-driven, object oriented apps to data-driven, reactive apps was very much mandated by the ever tougher requirements put on our apps. In an asynchronous and uncertain environment, the old delegate pattern just doesn’t cut it. Luckily, this is exactly where FRP frameworks shine.
The State Synchronization Problem
A common part of a programmer’s job is to keep the UI state synchronized with the Model state. Consider a simple messaging app. Either I type a message and hit send, at which point the message text should propagate from the View layer into the Model layer and subsequently to the server. Or the other person sends a message, which triggers a data change on the server and my app must observe that change and update the UI. We need some code to handle both of these cases.
One approach we can take is to write arbitrary synchronization logic. I.e. when the send button is tapped, the Controller takes the current text from the textfield, and performs the necessary changes on the Model, e.g. updates data in a database. It is also the Controller’s responsibility to post the message to the server. The other direction, i.e. receiving a message and updating the view, is also handled in the Controller in various ways. Note that, in the extreme case, almost no code is shared between the two branches that handle sending and receiving a message, respectivelly. Also note that the Controller has complete freedom over what it does, e.g. one implementation could choose to synchronize with the model layer after each letter typed, another might wait until the button is tapped and only query the current UI (textfield) state then.
Reactive programming comes in when we stop thinking about our app as a set of usecases with a codebranch handling each. When we instead think of the UI as having a state, the world (database, server) as having a state, and the programmer’s job as synchronizing these different states. We are concerned with data and its propagation (i.e. dataflow). How do I make sure that anytime a message appears on the server, it appears in the UI too? How do I make sure that when the UI says a new message should be sent, the database and server states get updated? Data binding technologies lend themselves to this task, allowing the programmer to define a declarative description of the dataflow, effectively turning the app into an automaton which, once set up, can do the right thing for all possible future inputs.
Reactive frameworks provide us with the machinery needed to describe apps in terms of data bindings. Being concerned only with dataflow, we can write more declarative code, avoid most side-effects and embrace functional programming. Once we have this tool, it might seem like an obvious silver bullet to use two-way data bindings for everything. The UI always syncs to the Model, the Model always syncs to the UI, everybody’s happy. This approach was popularized some years ago in the old Objective-C version of the ReactiveCocoa framework. However, it turns out that properly defining a graph of two-way bindings for the whole app is problematic, so one-way bindings using an FRP framework became the standard way to do reactive programming. This unidirectionality of bindings works well in situations where we consider some state (typically the UI state) to be derived from other state (typically the model state), which is what we usually want anyway.
This is the core of the paradigm change we’ve witnessed over the past 5 years. It brought about new frameworks, new app architectures and, perhaps most importantly, better apps. Read on to dive deeper into some of the subtleties of FRP, and find out how we use it on different platforms.
As Yaron Minsky writes in his blog at Jane Street, an FRP program is basically a dependency graph of signals, where each signal is either an external input or a derived signal that feeds off of other signals that have already been defined.
Pure Monadic FRP
In this full-featured version of FRP, signal combinators include
map2 for plain signal transformations,
join to support dynamism, and
foldp for history sensitivity (i.e. to be able to access past values of a signal as well as the current value). Unfortunately, pure monadic FRP has unacceptable space efficiency and is therefore unusable in practice.
Pure Applicative FRP
By getting rid of dynamism (and the
join operator), pure applicative FRP bypasses some of the difficulties related to evaluating the same expression at different times. This is the approch taken by Elm, and similar results can also be achieved by combining React and Redux, which we do on the web. Unfortunatelly, pure applicative FRP frameworks have proven problematic for use on mobile platforms, where VDOM isn’t a very nice way to render the UI, although react-native suggests that it’s good enough for some applications.
Impure Monadic FRP
Impure monadic FRP is the mainstream approach to FRP mostly popularized by the Rx family of frameworks and ReactiveSwift. It preserves most of the features of pure monadic FRP, with the caveat that the same expression evaluated at different times might return different values. This essentially turns any subscription to a signal into a potential side-effect. Even so, this is the most widely used kind of FRP. We use it on mobile with architectures like MVVM and MVP, as well as on backend. It also combines quite nicely with legacy code and the existing object oriented UI frameworks.
Self Adjusting Computation
Having first appeared in the OCaml framework Incremental, this little known twist on FRP has a lot going for it. It drops history-sensitivity (the
foldp operator), making you only rely on current values of signals. What you get in exchange is a very stable and predictable system, without reactive glitches. Research is being done by Chris Eidhof and others on how to tie a redux-like store with UIKit using a Swift port of the Incremental framework. The first incremental app has already been submitted to the AppStore at the time of publishing this article. Perhaps in the future we can use this approach to finally achieve unidirectional dataflow on mobile.