What is (functional) reactive programming?
Asked Answered
M

18

1147

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.

  1. What does functional reactive programming (FRP) mean in practice?
  2. 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.

Monteith answered 22/6, 2009 at 16:41 Comment(8)
here's a guy with an active imagination and good storytelling skills take on the whole thing. paulstovell.com/reactive-programmingParquetry
Different kinds of semantics can be found in Maximum Expressive Power with Minimal Construction. There is no mention about reactive programming except all program symbols calling other symbols activate their sub-functions in their automata and after finishing return their result if any. This is a simple call-return-phenomenon, which I regard as reactive programming, too.Battery
Somebody really needs to write a "Functional Reactive Programming For Dummies" for all us autodidacts out here. Every resource I've found, even Elm, seems to assume you've gotten a Master's in CS in the last five years. Those knowledgable about FRP seem to have completely lost the ability to see the matter from the naive viewpoint, something critical to teaching, training and evangelizing.Arium
Also see https://mcmap.net/q/10159/-what-is-functional-reactive-programmingChronicle
Another excellent FRP intro: The introduction to Reactive Programming you've been missing by my colleague AndréMartyrize
One of the best i have seen, Example based: gist.github.com/staltz/868e7e9bc2a7b8c1f754Christabella
FRP is to imperative programming what relativity (4D spacetime) is to classical mechanics (3D space + time) :)Supermundane
I find the spreadsheet analogy very helpful as a first rough impression (see Bob's answer: https://mcmap.net/q/10159/-what-is-functional-reactive-programming). A spreadsheet cell reacts to changes in other cells (pulls) but doesn't reach out and change others (doesn't push). The end result is that you can change one cell and a zillion others 'independently' update their own displays.Moulder
P
930

If you want to get a feel for FRP, you could start with the old Fran tutorial from 1998, which has animated illustrations. For papers, start with Functional Reactive Animation and then follow up on links on the publications link on my home page and the FRP link on the Haskell wiki.

Personally, I like to think about what FRP means before addressing how it might be implemented. (Code without a specification is an answer without a question and thus "not even wrong".) So I don't describe FRP in representation/implementation terms as Thomas K does in another answer (graphs, nodes, edges, firing, execution, etc). There are many possible implementation styles, but no implementation says what FRP is.

I do resonate with Laurence G's simple description that FRP is about "datatypes that represent a value 'over time' ". Conventional imperative programming captures these dynamic values only indirectly, through state and mutations. The complete history (past, present, future) has no first class representation. Moreover, only discretely evolving values can be (indirectly) captured, since the imperative paradigm is temporally discrete. In contrast, FRP captures these evolving values directly and has no difficulty with continuously evolving values.

FRP is also unusual in that it is concurrent without running afoul of the theoretical & pragmatic rats' nest that plagues imperative concurrency. Semantically, FRP's concurrency is fine-grained, determinate, and continuous. (I'm talking about meaning, not implementation. An implementation may or may not involve concurrency or parallelism.) Semantic determinacy is very important for reasoning, both rigorous and informal. While concurrency adds enormous complexity to imperative programming (due to nondeterministic interleaving), it is effortless in FRP.

So, what is FRP? You could have invented it yourself. Start with these ideas:

  • Dynamic/evolving values (i.e., values "over time") are first class values in themselves. You can define them and combine them, pass them into & out of functions. I called these things "behaviors".

  • Behaviors are built up out of a few primitives, like constant (static) behaviors and time (like a clock), and then with sequential and parallel combination. n behaviors are combined by applying an n-ary function (on static values), "point-wise", i.e., continuously over time.

  • To account for discrete phenomena, have another type (family) of "events", each of which has a stream (finite or infinite) of occurrences. Each occurrence has an associated time and value.

  • To come up with the compositional vocabulary out of which all behaviors and events can be built, play with some examples. Keep deconstructing into pieces that are more general/simple.

  • So that you know you're on solid ground, give the whole model a compositional foundation, using the technique of denotational semantics, which just means that (a) each type has a corresponding simple & precise mathematical type of "meanings", and (b) each primitive and operator has a simple & precise meaning as a function of the meanings of the constituents. Never, ever mix implementation considerations into your exploration process. If this description is gibberish to you, consult (a) Denotational design with type class morphisms, (b) Push-pull functional reactive programming (ignoring the implementation bits), and (c) the Denotational Semantics Haskell wikibooks page. Beware that denotational semantics has two parts, from its two founders Christopher Strachey and Dana Scott: the easier & more useful Strachey part and the harder and less useful (for software design) Scott part.

If you stick with these principles, I expect you'll get something more-or-less in the spirit of FRP.

Where did I get these principles? In software design, I always ask the same question: "what does it mean?". Denotational semantics gave me a precise framework for this question, and one that fits my aesthetics (unlike operational or axiomatic semantics, both of which leave me unsatisfied). So I asked myself what is behavior? I soon realized that the temporally discrete nature of imperative computation is an accommodation to a particular style of machine, rather than a natural description of behavior itself. The simplest precise description of behavior I can think of is simply "function of (continuous) time", so that's my model. Delightfully, this model handles continuous, deterministic concurrency with ease and grace.

It's been quite a challenge to implement this model correctly and efficiently, but that's another story.

Petta answered 23/6, 2009 at 4:31 Comment(21)
I have been aware of functional reactive programming. It seems related to my own research (in interactive statistical graphics) and I'm sure many of the ideas would be helpful for my work. However, I find it very difficult to get past the language - must I really learn about "denotational semantics" and "type class morphisms" to understand what's going on? A general audience introduction to the topic would be very useful.Nassi
@Conal: you clearly know what you're talking about, but your language presumes I have a doctorate in computational mathematics, which I do not. I do have a background in systems engineering and 20+ years of experience with computers and programming languages, still I feel your response leaves me baffled. I challenge you to repost your reply in english ;-)Lennielenno
@minplay.dk: Your remarks don't give me much to go on about what in particular you don't understand, and I'm disinclined to make wild guesses about what particular subset of English you're looking for. However, I invite you to say specifically what aspects of my explanation above you're having tripping up on, so that I and others can help you out. For instance, are there particular words you'd like defined or concepts for which you'd like references added? I really do like improving the clarity and accessibility of my writing--without dumbing it down.Petta
I think there are many unfamiliar terms, which would benefit from being briefly explained. For example, what does determinate and semantic determinancy mean in terms of concurrency? Compositional vocabulary? If you gave an explanatory treatment to all these new terms, as you did with 'denotational semantics', I think it would be far less obtuse to read.Ringdove
"Determinacy"/"determinate" means there's a single, well-defined correct value. In contrast, almost all forms of imperative concurrency can give different answers, depending on a scheduler or whether you're looking or not, and they can even deadlock. "Semantic" (and more specifically "denotational") refers to the value ("denotation") of an expression or representation, in contrast to "operational" (how the answer is computed or how much space and/or time is consumed by what kind of machine).Petta
"Compositional" means something composes, i.e., is constructed building-block style, which is how we work with numbers, using arithmetic, powers/roots, logarithms, trig, etc. "Vocabulary" is a collection of atomic terms and combining operators/functions.Petta
I agree with @Lennielenno although I can't brag of having been in the field for very long. Even though it seemed like you know what you're talking about, it didn't give me a quick, brief and simple understanding of what this is, as I'm spoiled enough to expect on SO. This answer primarily drove me to a ton of new questions without really answering my first one. I'm hoping that sharing the experience of still being relatively ignorant in the field can give you an insight of how simple and brief you really need to be. I come from a similar background as the OP, btw.Recitative
Sure if you were interested enough and had a few days of spare time to invest only in learning this thing, it has nice depth to it, but it's generally more helpful to get a simple answer. If I get curious about other details then I Google that question and is likely to find another SO answer where that specific thing was explained, and like that it all chains together perfectly. That's one of the reasons I think simple and brief examples are key to teaching/answering. Hope this helped you.Recitative
@AskeB. I think the problem with this concept is that it depends on lots of big ideas that mathematicians precisely defined in those words you don’t know the meaning of. So in order to grasp it you need to know (at least most) of the referenced concepts. If you do it is (nearly) trivial understanding the concept. If you don’t, you will positively not get the essence without learning about the referenced concepts first. It is how it is.Rudie
@AskeB. That’s what these advanced ideas are all about. You can understand the idea behind e.g. C in a few hours but soon complex problems will appear. In order to solve those, you need more advanced concepts. This is one that is very advanced and still a state of the art research topic (with very few implementations and even less programs that use it).Rudie
@AskeB. I could ask you if you have already grasped things like polymorphism and inheritance (which are completely separate concepts btw.) and that they are not dependent of OO, &c. Those represent the /essence/ of things, as does the above answer. Things like Java, C++, Haskell are just implementations of ideas. And you don’t necessarily have to understand the concepts in order to use the implementations. Neither does the implementor have to (sadly). I would claim that a lot of programmers don’t.Rudie
Here's a 'dumbed down' interpretation of Conal's last paragraph: Conal likes the look of declarative style of programming - which is a consequence of defining the language syntax based on 'meanings' rather than sequences of instructions. Imperative programming was suited to instructing e.g. von Neumann architectures (with clear distinction between CPU and RAM), not to describe the changes in the VALUES we really care about (let's call these 'functions'). i.e. If you ask a functional programmer and an imperative programmer what a 'variable' is, you'll get completely different answers.Pytlik
FRP is the concept of expressing program behaviour as a mathematical function that maps Time values to Output values (whether this be graphics, music or what have you). Simplest example: A very rudimentary video player is a program that can be expressed as a function that maps milliseconds onto the picture that should be displayed AT THAT MILLISECOND. e.g. in Java syntax something like public Picture frameForTime(float millisOffsetFromStartOfVideo) { // Much decoding from video file... return frame; } BUT THE FUNCTION MUST NOT HAVE SIDE-EFFECTS.Pytlik
And yes, FRP programs too must execute on real computers, unfortunately. So there are some challenges when it comes to performance or utilising other bits of lovely hardware architecture available to your program (e.g. a DSP or a GPGPU) but that is besides the point as this is implementation-specific.Pytlik
Is FRP concept same as the "Reactive Applications" concept described in the reactive manifesto? Or is it just an unfortunate clash of terminology?Defensive
Just a clash, they don't mention time dependent functions anywhere there now do they?Tc
Unfortunately "general audience" is even harder to grasp than Conal's answer! When something is not intuitive to me, it might mean that I need to work harder, not blame the author for failing to cater to me in particular.Hostel
@Jus12: I think the techniques of reactive manifesto lacks both fundamental FRP principles (precise denotation and continuous time). FRP may have had an influence (or not), but the two are coming from very different perspectives.Petta
I think its amazing you can say all that and leave me no more informed than I was at the start. Are you able to break down these lofty concepts into functional language to enable a reader to discern the various pros/cons of FRP, the problem they intend to solve and their benefits over existing patterns?Cleaning
You spoke for me.:)Saccharoid
Having a few years experiences in C++ programming though. With such a high voted answer, still having difficulty understanding what's FRP. Feeling sad... :(Derangement
M
737

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):

x = <mouse-x>;
y = <mouse-y>;

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:

minX = x - 16;
minY = y - 16;
maxX = x + 16;
maxY = y + 16;

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:

rectangle(minX, minY, maxX, maxY)

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.

Misanthrope answered 22/6, 2009 at 18:6 Comment(21)
So reactive programming is a form of declarative programming then?Changchangaris
d.drawRectangle(minX, minY, maxX, maxY); Aside: functional behavior fits nicely with functional graphics: rectangle(minX, minY, maxX, maxY), which would be an expression rather than a statement.Petta
> So reactive programming is a form of declarative programming then? Functional reactive programming is a form of functional programming, which is a form of declarative programming.Petta
@Conal: Right, functional graphics make more sense. I'm not sure why I used an imperative style for the rectangle drawing bit.Misanthrope
So it is as if You wrote #define x mouse_x() in C?Larousse
@Larousse Not really, no. For example, if I call sqrt(x) in C with your macro, that just computes sqrt(mouse_x()) and gives me back a double. In a true functional reactive system, sqrt(x) would return a new "double over time". If you were to try to simulate an FR system with #define you'd pretty much have to swear off variables in favor of macros. FR systems will also typically only recalculate stuff when it needs to be recalculated, while using macros would mean you'd be constantly re-evaluating everything, all the way down to the subexpressions.Misanthrope
"For many types of software (for example, anything with user interaction) side-effects are necessary at some level." And perhaps only at the implementation level. There are a lot of side-effects in the implementation of pure, lazy functional programming, and one of the successes of the paradigm is to keep many of those effects out of programming model. My own forays into functional user interfaces suggest that they also can be programmed entirely without side-effects.Petta
@Conal: Isn't interacting with the user a side effect?Stridulate
"reactive programming removes the need to mutate variables while still letting you do a lot of what you could accomplish with variable mutations" Seems like the exact opposite is happening to me. x is changing over time, how is that different from mutating it?Stelmach
This description reminds me of (synthesizable) verilog. Thought I'd mention that in case it helps someone else understand the concept.Disavow
@tieTYT x is never reassigned/mutated. x's value is the sequence of values over time. Another way to look at it is that instead of x having a "normal" value, like a number, x's value is (conceptually) a function that takes time as a parameter. (This is a bit of an oversimplification. You can't create time values that would allow you to predict the future of things like the mouse position.)Misanthrope
I may still be completely missing the point here, but is the idea here to update every variable each tick? Wouldn't this have the potential to have a large performance hit? Eg updating x, y, minX, minY, maxX, maxY, + other variables every tick could get expensive. I suspect I completely misunderstood your answer, however, and will do more research.Bascio
Don't 'functional' and 'reactive' pretty much cancel eachother out? I don't see how FRP is functional, with the most magic, unpredictable variables ever. No side effects!? You'd have no idea what value a variable would have and why?Saponin
@Rudie, If we describe those magic integer-over-times as functions of time, some of this functions will be pure, but others not. Function of mouse pointer position is definetely unpure, in fact it doesn't even use time variable. So I look rather sceptic to this idea, because you can't make "pure" gui like this.Juneberry
Is there actually a language like the example one in this answer available to muck around with?Underproof
@Underproof "Elm is a functional reactive programming (FRP) language that compiles to HTML, CSS, and JS." elm-lang.orgSlavey
@TheIronKnuckle: there is also Bacon for javascriptBaa
I've got about two dozen tabs open on FRP right now and this is by far the most succinct explanation I've found. You should consider writing more extensively on the matter e.g. a book. It's a hot topic and as I noted on my comment on the parent post, all existing materials are written blind to the naive view of the subject.Arium
I wonder how many people found this more useful only AFTER reading the accepted answer and not realizing that the accepted answer prepped them to understand this.Manuelmanuela
@Saponin I think the reason FRP is still pure, is that given the same inputs, the entire system will always be exactly the same, no matter what came before it. So in the rectangle example, x and y are functions over mouse input. If you put the mouse back to where it used to be, you've got the same system, you're not mutating back to the previous system, it's simply that you are recalculating the same inputs and getting the same output. Also, I think when doing FRP, you could remember the history, so all variables are like a trail.Trenchant
@DidierA. Keeping state is a mechanism for succinctly describing a complex function over large input and output spaces, by decomposing the function into several simpler functions that are conditioned on the state value. FRP goes back to the original situation where the functions are complex and the input and output spaces are huge. This was always possible, since the entire state space can be rolled into the input space. But it's usually stupid to do so.Stafani
J
144

An easy way of reaching a first intuition about what it's like is to imagine your program is a spreadsheet and all of your variables are cells. If any of the cells in a spreadsheet change, any cells that refer to that cell change as well. It's just the same with FRP. Now imagine that some of the cells change on their own (or rather, are taken from the outside world): in a GUI situation, the position of the mouse would be a good example.

That necessarily misses out rather a lot. The metaphor breaks down pretty fast when you actually use a FRP system. For one, there are usually attempts to model discrete events as well (e.g. the mouse being clicked). I'm only putting this here to give you an idea what it's like.

Jemmy answered 23/6, 2009 at 14:52 Comment(2)
An extremely apposite example. It's great to have the theoretical stuff, and perhaps some people get the implications of that without recourse to a grounding example, but I need to start with what it does for me, not what it abstractly is. What I only recently got (from the Rx talks by Netflix!) is that RP (or Rx, anyway), makes these "changing values" first class and lets you reason about them, or write functions that do things with them. Write functions to create spreadsheets or cells, if you like. And it handles when a value ends (goes away) and lets you clean up automatically.Hollowell
This example emphasizes the difference between event-driven programming and reactive approach, where you just declare the dependencies to use intelligent routing.Dichotomy
L
131

To me it is about 2 different meanings of symbol =:

  1. In math x = sin(t) means, that x is different name for sin(t). So writing x + y is the same thing as sin(t) + y. Functional reactive programming is like math in this respect: if you write x + y, it is computed with whatever the value of t is at the time it's used.
  2. In C-like programming languages (imperative languages), x = sin(t) is an assignment: it means that x stores the value of sin(t) taken at the time of the assignment.
Larousse answered 25/5, 2012 at 14:52 Comment(3)
Good explanation. I think you could also add that "time" in the sense of FRP is normally "any change from external input". Anytime an external force changes an input of FRP, you've moved "time" forward, and recalculate everything again that is affected by the change.Trenchant
In math x = sin(t) means x is the value of sin(t) for the given t. It is not a different name for sin(t) as function. Otherwise it would be x(t) = sin(t).Shem
+Dmitri Zaitsev Equals sign has several meanings in mathematics. One of them is that whenever You see left side You can swap it with right side. For example 2 + 3 = 5 or a**2 + b**2 = c**2.Larousse
T
69

OK, from background knowledge and from reading the Wikipedia page to which you pointed, it appears that reactive programming is something like dataflow computing but with specific external "stimuli" triggering a set of nodes to fire and perform their computations.

This is pretty well suited to UI design, for example, in which touching a user interface control (say, the volume control on a music playing application) might need to update various display items and the actual volume of audio output. When you modify the volume (a slider, let's say) that would correspond to modifying the value associated with a node in a directed graph.

Various nodes having edges from that "volume value" node would automatically be triggered and any necessary computations and updates would naturally ripple through the application. The application "reacts" to the user stimulus. Functional reactive programming would just be the implementation of this idea in a functional language, or generally within a functional programming paradigm.

For more on "dataflow computing", search for those two words on Wikipedia or using your favorite search engine. The general idea is this: the program is a directed graph of nodes, each performing some simple computation. These nodes are connected to each other by graph links that provide the outputs of some nodes to the inputs of others.

When a node fires or performs its calculation, the nodes connected to its outputs have their corresponding inputs "triggered" or "marked". Any node having all inputs triggered/marked/available automatically fires. The graph might be implicit or explicit depending on exactly how reactive programming is implemented.

Nodes can be looked at as firing in parallel, but often they are executed serially or with limited parallelism (for example, there may be a few threads executing them). A famous example was the Manchester Dataflow Machine, which (IIRC) used a tagged data architecture to schedule execution of nodes in the graph through one or more execution units. Dataflow computing is fairly well suited to situations in which triggering computations asynchronously giving rise to cascades of computations works better than trying to have execution be governed by a clock (or clocks).

Reactive programming imports this "cascade of execution" idea and seems to think of the program in a dataflow-like fashion but with the proviso that some of the nodes are hooked to the "outside world" and the cascades of execution are triggered when these sensory-like nodes change. Program execution would then look like something analogous to a complex reflex arc. The program may or may not be basically sessile between stimuli or may settle into a basically sessile state between stimuli.

"non-reactive" programming would be programming with a very different view of the flow of execution and relationship to external inputs. It's likely to be somewhat subjective, since people will likely be tempted to say anything that responds to external inputs "reacts" to them. But looking at the spirit of the thing, a program that polls an event queue at a fixed interval and dispatches any events found to functions (or threads) is less reactive (because it only attends to user input at a fixed interval). Again, it's the spirit of the thing here: one can imagine putting a polling implementation with a fast polling interval into a system at a very low level and program in a reactive fashion on top of it.

Tinctorial answered 22/6, 2009 at 17:45 Comment(9)
OK, there are some good answers up above now. Should I remove my post? If I see two or three people saying it adds nothing, I'll delete it unless its helpful count goes up. No point in leaving it here unless it adds something of value.Tinctorial
you have mentioned data flow, so that adds some value IMHO.Rhamnaceous
That's what QML is meant to be, it seems ;)Highpriced
An example would be the MAX/MSP development environment, popular among multimedia artists dealing with audio and video processing.Gies
For me, this answer was the easiest to understand, especially because the use of natural analogues like "ripple through the application" and "sensory-like nodes". Great!Conjoined
Please keep it around. It is helping. When you're wrapping your brain around something new, multiple perspectives help it eventually click.Stricker
Using graphs to abstract concepts is one of the best things in computer science. It is easy to understand a concept when putting it as a graph IMHO.Botulin
unfortunately, the Manchester Dataflow Machine link is dead.Enjoin
Thanks Pac0. I found and alternative link to one of the original papers about it and added that link.Tinctorial
T
65

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).

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.

So, in my understanding an FRP program is a set of equations: enter image description here

j is discrete: 1,2,3,4...

f depends on t so this incorporates the possiblilty to model external stimuli

all state of the program is encapsulated in variables x_i

The FRP library takes care of progressing time, in other words, taking j to j+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 the x_i changes at time j then not all the other x_i' values at j+1 need to be updated, so not all the dependencies need to be recalculated because some x_i' might be independent from x_i.

Furthermore, x_i-s that do change can be incrementally updated. For example let's consider a map operation f=g.map(_+1) in Scala, where f and g are List of Ints. Here f corresponds to x_i(t_j) and g is x_j(t_j). Now if I prepend an element to g then it would be wasteful to carry out the map operation for all the elements in g. 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 (the x_i-s) if some of the f_i-s do change.

Tc answered 31/1, 2015 at 3:46 Comment(4)
I was right there with you until you went with discrete equations. The founding idea of FRP was continuous time, where there is no "j+1". Instead, think of functions of continuous time. As Newton, Leibniz, and others showed us, it's often deeply handy (and "natural" in a literal sense) to describe these functions differentially, but continuously so, using integrals and systems of ODEs. Otherwise, you're describing an approximation algorithm (and a poor one) instead of the thing itself.Petta
The HTML templating and layout constraints language layx seems to express elements of FRP.Zilber
@Petta this makes me wonder how is FRP different from ODEs. How do they differ ?Tc
@Tc In that integration (possibly recursive, i.e., ODEs) provides one of the building blocks of FRP, not the entirety. Every element of the FRP vocabulary (including but not limited to integration) is precisely explained in terms of continuous time. Does that explanation help?Petta
L
29

The paper Simply efficient functional reactivity by Conal Elliott (direct PDF, 233 KB) is a fairly good introduction. The corresponding library also works.

The paper is now superceded by another paper, Push-pull functional reactive programming (direct PDF, 286 KB).

Lew answered 22/6, 2009 at 17:48 Comment(0)
S
28

Disclaimer: my answer is in the context of rx.js - a 'reactive programming' library for Javascript.

In functional programming, instead of iterating through each item of a collection, you apply higher order functions (HoFs) to the collection itself. So the idea behind FRP is that instead of processing each individual event, create a stream of events (implemented with an observable*) and apply HoFs to that instead. This way you can visualize the system as data pipelines connecting publishers to subscribers.

The major advantages of using an observable are:
i) it abstracts away state from your code, e.g., if you want the event handler to get fired only for every 'n'th event, or stop firing after the first 'n' events, or start firing only after the first 'n' events, you can just use the HoFs (filter, takeUntil, skip respectively) instead of setting, updating and checking counters.
ii) it improves code locality - if you have 5 different event handlers changing the state of a component, you can merge their observables and define a single event handler on the merged observable instead, effectively combining 5 event handlers into 1. This makes it very easy to reason about what events in your entire system can affect a component, since it's all present in a single handler.

  • An Observable is the dual of an Iterable.

An Iterable is a lazily consumed sequence - each item is pulled by the iterator whenever it wants to use it, and hence the enumeration is driven by the consumer.

An observable is a lazily produced sequence - each item is pushed to the observer whenever it is added to the sequence, and hence the enumeration is driven by the producer.

Sneed answered 26/5, 2014 at 17:10 Comment(2)
Thank you so much for this straightforward definition of an observable and its differentiation from iterables. I think it is often very helpful to compare a complex concept with its well-known dual concept to gain a true understanding.Corody
"So the idea behind FRP is that instead of processing each individual event, create a stream of events (implemented with an observable*) and apply HoFs to that instead." I could be mistaken but I believe this is not actually FRP but rather a nice abstraction over the Observer design pattern which allows for functional operations via HoF (which is great!) while still intended to be used with imperative code. Discussion on the topic - lambda-the-ultimate.org/node/4982Lemnos
P
18

Dude, this is a freaking brilliant idea! Why didn't I find out about this back in 1998? Anyway, here's my interpretation of the Fran tutorial. Suggestions are most welcome, I am thinking about starting a game engine based on this.

import pygame
from pygame.surface import Surface
from pygame.sprite import Sprite, Group
from pygame.locals import *
from time import time as epoch_delta
from math import sin, pi
from copy import copy

pygame.init()
screen = pygame.display.set_mode((600,400))
pygame.display.set_caption('Functional Reactive System Demo')

class Time:
    def __float__(self):
        return epoch_delta()
time = Time()

class Function:
    def __init__(self, var, func, phase = 0., scale = 1., offset = 0.):
        self.var = var
        self.func = func
        self.phase = phase
        self.scale = scale
        self.offset = offset
    def copy(self):
        return copy(self)
    def __float__(self):
        return self.func(float(self.var) + float(self.phase)) * float(self.scale) + float(self.offset)
    def __int__(self):
        return int(float(self))
    def __add__(self, n):
        result = self.copy()
        result.offset += n
        return result
    def __mul__(self, n):
        result = self.copy()
        result.scale += n
        return result
    def __inv__(self):
        result = self.copy()
        result.scale *= -1.
        return result
    def __abs__(self):
        return Function(self, abs)

def FuncTime(func, phase = 0., scale = 1., offset = 0.):
    global time
    return Function(time, func, phase, scale, offset)

def SinTime(phase = 0., scale = 1., offset = 0.):
    return FuncTime(sin, phase, scale, offset)
sin_time = SinTime()

def CosTime(phase = 0., scale = 1., offset = 0.):
    phase += pi / 2.
    return SinTime(phase, scale, offset)
cos_time = CosTime()

class Circle:
    def __init__(self, x, y, radius):
        self.x = x
        self.y = y
        self.radius = radius
    @property
    def size(self):
        return [self.radius * 2] * 2
circle = Circle(
        x = cos_time * 200 + 250,
        y = abs(sin_time) * 200 + 50,
        radius = 50)

class CircleView(Sprite):
    def __init__(self, model, color = (255, 0, 0)):
        Sprite.__init__(self)
        self.color = color
        self.model = model
        self.image = Surface([model.radius * 2] * 2).convert_alpha()
        self.rect = self.image.get_rect()
        pygame.draw.ellipse(self.image, self.color, self.rect)
    def update(self):
        self.rect[:] = int(self.model.x), int(self.model.y), self.model.radius * 2, self.model.radius * 2
circle_view = CircleView(circle)

sprites = Group(circle_view)
running = True
while running:
    for event in pygame.event.get():
        if event.type == QUIT:
            running = False
        if event.type == KEYDOWN and event.key == K_ESCAPE:
            running = False
    screen.fill((0, 0, 0))
    sprites.update()
    sprites.draw(screen)
    pygame.display.flip()
pygame.quit()

In short: If every component can be treated like a number, the whole system can be treated like a math equation, right?

Philae answered 13/3, 2011 at 9:44 Comment(1)
This is a bit late, but anyways... Frag is a game using FRP.Usurpation
S
14

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.

Suspensory answered 24/6, 2009 at 18:41 Comment(0)
Y
10

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: TX, 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.

Yaya answered 17/7, 2015 at 1:9 Comment(0)
G
9

Acts like a spreadsheet as noted. Usually based on an event driven framework.

As with all "paradigms", it's newness is debatable.

From my experience of distributed flow networks of actors, it can easily fall prey to a general problem of state consistency across the network of nodes i.e. you end up with a lot of oscillation and trapping in strange loops.

This is hard to avoid as some semantics imply referential loops or broadcasting, and can be quite chaotic as the network of actors converges (or not) on some unpredictable state.

Similarly, some states may not be reached, despite having well-defined edges, because the global state steers away from the solution. 2+2 may or may not get to be 4 depending on when the 2's became 2, and whether they stayed that way. Spreadsheets have synchronous clocks and loop detection. Distributed actors generally don't.

All good fun :).

Goss answered 4/9, 2013 at 11:10 Comment(0)
S
8

I found this nice video on the Clojure subreddit about FRP. It is pretty easy to understand even if you don't know Clojure.

Here's the video: http://www.youtube.com/watch?v=nket0K1RXU4

Here's the source the video refers to in the 2nd half: https://github.com/Cicayda/yolk-examples/blob/master/src/yolk_examples/client/autocomplete.cljs

Stelmach answered 17/5, 2013 at 21:18 Comment(0)
D
7

This article by Andre Staltz is the best and clearest explanation I've seen so far.

Some quotes from the article:

Reactive programming is programming with asynchronous data streams.

On top of that, you are given an amazing toolbox of functions to combine, create and filter any of those streams.

Here's an example of the fantastic diagrams that are a part of the article:

Click event stream diagram

Destructor answered 5/11, 2016 at 2:4 Comment(0)
S
5

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

Subclimax answered 17/6, 2015 at 23:15 Comment(1)
This is exactly how I feel about declarative programming, and you just describe the idea better than me.Weaponry
L
2

The short and clear explanation about Reactive Programming appears on Cyclejs - Reactive Programming, it uses simple and visual samples.

A [module/Component/object] is reactive means it is fully responsible for managing its own state by reacting to external events.

What is the benefit of this approach? It is Inversion of Control, mainly because [module/Component/object] is responsible for itself, improving encapsulation using private methods against public ones.

It is a good startup point, not a complete source of knowlege. From there you could jump to more complex and deep papers.

Ligniform answered 5/3, 2017 at 10:8 Comment(0)
S
0

Check out Rx, Reactive Extensions for .NET. They point out that with IEnumerable you are basically 'pulling' from a stream. Linq queries over IQueryable/IEnumerable are set operations that 'suck' the results out of a set. But with the same operators over IObservable you can write Linq queries that 'react'.

For example, you could write a Linq query like (from m in MyObservableSetOfMouseMovements where m.X<100 and m.Y<100 select new Point(m.X,m.Y)).

and with the Rx extensions, that's it: You have UI code that reacts to the incoming stream of mouse movements and draws whenever you're in the 100,100 box...

Sprightly answered 25/10, 2016 at 15:59 Comment(0)
W
0

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.

Wimsatt answered 8/12, 2017 at 18:51 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.