I've read the Wikipedia article on reactive programming. I've also read the small article on functional reactive programming. The descriptions are quite abstract.
- What does functional reactive programming (FRP) mean in practice?
- What does reactive programming (as opposed to non-reactive programming?) consist of?
My background is in imperative/OO languages, so an explanation that relates to this paradigm would be appreciated.
Paul Hudak's book, The Haskell School of Expression, is not only a fine introduction to Haskell, but it also spends a fair amount of time on FRP. If you're a beginner with FRP, I highly recommend it to give you a sense of how FRP works.
There is also what looks like a new rewrite of this book (released 2011, updated 2014), The Haskell School of Music.
FRP is a combination of Functional programming(programming paradigm built upon the idea of everything is a function) and reactive programming paradigm (built upon the idea that everything is a stream(observer and observable philosophy)). It is supposed to be the best of the worlds.
Check out Andre Staltz post on reactive programming to start with.
In pure functional programming, there are no side-effects. For many types of software (for example, anything with user interaction) side-effects are necessary at some level.
One way to get side-effect like behavior while still retaining a functional style is to use functional reactive programming. This is the combination of functional programming, and reactive programming. (The Wikipedia article you linked to is about the latter.)
The basic idea behind reactive programming is that there are certain datatypes that represent a value "over time". Computations that involve these changing-over-time values will themselves have values that change over time.
For example, you could represent the mouse coordinates as a pair of integer-over-time values. Let's say we had something like (this is pseudo-code):
At any moment in time, x and y would have the coordinates of the mouse. Unlike non-reactive programming, we only need to make this assignment once, and the x and y variables will stay "up to date" automatically. This is why reactive programming and functional programming work so well together: reactive programming removes the need to mutate variables while still letting you do a lot of what you could accomplish with variable mutations.
If we then do some computations based on this the resulting values will also be values that change over time. For example:
In this example,
minX
will always be 16 less than the x coordinate of the mouse pointer. With reactive-aware libraries you could then say something like:And a 32x32 box will be drawn around the mouse pointer and will track it wherever it moves.
Here is a pretty good paper on functional reactive programming.
After reading many pages about FRP I finally came across this enlightening writing about FRP, it finally made me understand what FRP really is all about.
I quote below Heinrich Apfelmus (author of reactive banana).
So, in my understanding an FRP program is a set of equations:
j
is discrete: 1,2,3,4...f
depends ont
so this incorporates the possiblilty to model external stimuliall state of the program is encapsulated in variables
x_i
The FRP library takes care of progressing time, in other words, taking
j
toj+1
.I explain these equations in much more detail in this video.
EDIT:
About 2 years after the original answer, recently I came to the conclusion that FRP implementations have another important aspect. They need to (and usually do) solve an important practical problem: cache invalidation.
The equations for
x_i
-s describe a dependency graph. When some of thex_i
changes at timej
then not all the otherx_i'
values atj+1
need to be updated, so not all the dependencies need to be recalculated because somex_i'
might be independent fromx_i
.Furthermore,
x_i
-s that do change can be incrementally updated. For example let's consider a map operationf=g.map(_+1)
in Scala, wheref
andg
areList
ofInts
. Heref
corresponds tox_i(t_j)
andg
isx_j(t_j)
. Now if I prepend an element tog
then it would be wasteful to carry out themap
operation for all the elements ing
. Some FRP implementations (for example reflex-frp) aim to solve this problem. This problem is also known as incremental computing.In other words, behaviours (the
x_i
-s ) in FRP can be thought as cache-ed computations. It is the task of the FRP engine to efficiently invalidate and recompute these cache-s (thex_i
-s) if some of thef_i
-s do change.According to the previous answers, it seems that mathematically, we simply think in a higher order. Instead of thinking a value x having type X, we think of a function x: T → X, where T is the type of time, be it the natural numbers, the integers or the continuum. Now when we write y := x + 1 in the programming language, we actually mean the equation y(t) = x(t) + 1.
It is about mathematical data transformations over time (or ignoring time).
In code this means functional purity and declarative programming.
State bugs are a huge problem in the standard imperative paradigm. Various bits of code may change some shared state at different "times" in the programs execution. This is hard to deal with.
In FRP you describe (like in declarative programming) how data transforms from one state to another and what triggers it. This allows you to ignore time because your function is simply reacting to its inputs and using their current values to create a new one. This means that the state is contained in the graph (or tree) of transformation nodes and is functionally pure.
This massively reduces complexity and debugging time.
Think of the difference between A=B+C in math and A=B+C in a program. In math you are describing a relationship that will never change. In a program, its says that "Right now" A is B+C. But the next command might be B++ in which case A is not equal to B+C. In math or declarative programming A will always be equal to B+C no matter what point in time you ask.
So by removing the complexities of shared state and changing values over time. You program is much easier to reason about.
An EventStream is an EventStream + some transformation function.
A Behaviour is an EventStream + Some value in memory.
When the event fires the value is updated by running the transformation function. The value that this produces is stored in the behaviours memory.
Behaviours can be composed to produce new behaviours that are a transformation on N other behaviours. This composed value will recalculate as the input events (behaviours) fire.
"Since observers are stateless, we often need several of them to simulate a state machine as in the drag example. We have to save the state where it is accessible to all involved observers such as in the variable path above."
Quote from - Deprecating The Observer Pattern http://infoscience.epfl.ch/record/148043/files/DeprecatingObserversTR2010.pdf