Reactive programming is a paradigm that creates permanent relationships between values. Rather than one-time calculations, a reactive expression updates its result whenever the source value changes. Perhaps first popularized in spreadsheets, and seen commonly in stream form for audio processing, it's now become a valued tool in user interface programming and other feedback systems.
Why
Reactive expressions let us think about high-level relationships between data instead of dealing with the endless shuffling of data. Data is treated as an active structure where an output reflects the current state.
The paradigm deals well with data that frequently changes and is visible the user. It provides a clean bridge between the data and interface. It is a natural partner to declarative programming.
Reactive can also be seen as a higher level paradigm than event programming. At times when it does apply, reactive code will be substantially smaller and cleaner than the equivalent event code.
This article focuses on the benefits and core qualities of functional programming. As with all paradigms, there are limitations. I’ll have to look at those in a future article.
The basics
Consider a basic expression like below:
c = a + b
In a non-reactive paradigm, such as functional or imperative, the value of c
will be calculated only once: it's the sum of the two inputs a
and b
. In the reactive paradigm, this instead creates a permanent relationship. c
becomes a devoted follower of a
and b
, whenever either of those gets a new value c
will get a new value as well.
In a spreadsheet, this allows the creation of active formulas. The user can change the source values and all cells that rely on this value update automatically. The user doesn't worry about how that propagation occurs, focusing solely on the relationships between the data.
Feedback in interfaces
Reactive expressions are now common in user interfaces. They simplify getting data to the screen, and user input back to the data.
Let's look at a simple shopping list for a user. I'll use a simple declarative UI syntax, as it combines well with reactive expressions. Assume there is a source list called shoppingList
.
<Dialog>
<Field Title="Number of Items" Value="count(shoppingList)"/>
<Field Title="Total Cost" Value="sum( pick( shoppingList, 'cost' ) )"/>
<ForEach Items="shoppingList">
<Field Title="Name" Value="name"/>
<Field Title="Cost" Value="cost"/>
<PrioritySelection Value="priority"/>
</ForEach>
</Dialog>
If any part of the program adds a new item to shoppingList
, this display will automatically update. The count
will update to the new number of items, the sum
will add up the costs, and the ForEach
will display the new item in the list.
Now assume the user enters a coupon code giving them a discount on one of the items. The coupon processing code will update the cost
of one of the items, and the total cost will be recalculated.
In practice few frameworks provide 100% reactive data. Instead, data must be explicitly marked as "observable" or part of a reflective state. Even in a reactive application the majority of variables and code are not reactive. For efficiency, and syntax convenience, it makes sense to be selective.
Two-way
The focus in reactive programming is from the source to target data transformation. It is also possible for this relationship to be bidirectional. For example, the priority
field above can be presented as a drop-down selection for the user. If they update their priority for the item, it will be reflected back into the source data.
This two-way relationship can only be offered for simple expressions, such as binding a UI element directly to a value. Not all expression can be two-way. It doesn't make sense for the user to update the Number of items
field; there is no way to reverse the count
relationship.
Functional, or not really
Many reactive expressions look rather functional. It's a convenient form to express the relationships between data. We see a lot of functions like count
, map
, and sum
. Indeed, many references refer to the paradigm as "functional reactive programming", where the shorter "reactive" term applies to the lower-level mechanisms.
This doesn't mean we can't add imperative hooks into the reactive expressions. It's not uncommon to see code that listens for update events to make a bridge between reactive and non-reactive modules.
var pageData = Observable()
page.property.onChanged( function( newProperty ) {
//TODO: find and reprimand colleage that failed to make a reactive HTTP layer
var request = HTTP.create( baseUrl + "/user/" + newProprerty.user )
request.onSuccess( function( data ) {
pageData.set( data )
})
request.onError( function( err ) {
pageData.failed( err )
})
request.get()
})
Such bridges can be a bit problematic if the remote data can also change on its own. A full reactive integration would require extending the paradigm to the server -- the server would need to push updates to the client.
If we want to have some fun we can also introduce non-static expressions. We might want to do an authentication token that updates every 30 seconds:
var interval = Timer.seconds(30)
var authToken = interval.map( function(t) {
return totp.getToken(t)
})
Or we could connect directly to the sensors of a mobile device:
<P>In case of falling consult arrow for up direction.</P>
<Image File="arrow.png">
<Rotation Degrees="vectorToAngle( accelerometer.up )">
</Image>
These examples lead us to think that reactive programming is possibly a form of stream processing...
Stream processing
Audio stream processing is essentially* the same as reactive programming. We have various inputs connected through a graph of effects that eventually combine in the final output stream. The core data going through the nodes is audio data, but other types are certainly there. The various dials and sliders also feed into this network, as do any pedals or external switches.
*"essentially" here actually means the two are provably the same model, but instead of doing the dry theory here I'll just resort to a bit of hand-waving and hope you accept the conclusion.
Stream diagrams and terms are often used to describe the reactive approach. Some frameworks even offer operators that only make sense when explained as a stream. As a stream is perhaps the most generic form of the reactive model, it's not a bad idea to always think in those terms. Of course, it's also a bit weird to be thinking of a stream when the user is doing a one-time update of a text field.
Use where possible
When a reactive model applies to a domain, it is usually the best model, leading the others by a wide margin. It hides the complexity of keeping data in sync. The resulting code is easy to read and shows a clear picture of the relationships between data.
Reactive programming blends with other paradigms. It goes hand-in-hand with functional programming, often being called "functional reactive programming". It's a natural extension of event programming, and easy to integrate with imperative code. It's an excellent partner to declarative programming: there's an elegance in directly seeing the relationships between data.
Top comments (2)
I'm not fully understanding what stream programming is. So stream programming is when there are multiple inputs connected to an output stream which then sends the information back through the multiple inputs? Correct me where I'm wrong.
It might be easier to visualize as single-input to output scenario. I'll use audio since it's easy to understand as a stream.
You have an
amplify
function that makes the sound longer. You plugin the input stream to this "function" and it's output is a louder sound. The data is "streamed": input continually reachs this function and is continually output by the function.For the two input case you can have a
mix
function that combines them and produces one output.This is a form of reactive programming since you still have an output that automatically reflects the state of the input. Just in this case the input is continually changing.
For example, you might have a UI which connects to a CD player as follows:
cd_data -> decoder -> amplify -> fft -> graph_display
The
graph_display
is a reactive control that continually updates it's appearance based on the output offft
, which in turn connects backwards until it reaches the rawcd_data
.