
An introduction to Reactive Programming - jxub
https://gist.github.com/staltz/868e7e9bc2a7b8c1f754
======
thomastjeffery
> If you prefer to watch video tutorials with live-coding, then check out this
> series I recorded with the same contents as in this article

Thanks for actually writing. Written information can be parsed non-linearly,
which is super important for learning and reference. For whatever reason, a
lot of people don't seem to understand that, and just put out videos, which
are like lectures: helpful, but frustrating.

~~~
ivanhoe
Both can be useful - especially if video is with live coding. Texts often omit
the small details that are perhaps obvious to the author, but you are not
aware of them. IMHO every installation instruction out there that takes more
than copy&pasting 2 lines of text should be in a form of a video, because
there's always that one step that is not in the text...

~~~
thomastjeffery
It also helps to actively do the thing the way you expect the reader/viewer to
do it, so that you don't skip any steps, and you understand common pitfalls.

The problem with doing that, however, is that you are doing something a
specific way, on a specific platform, using specific tools. That can make it
frustrating or even impossible for viewers/readers to break out of your
specific workflow/tools/platform, which is usually what they want to do.

------
danharaj
My shop specializes in multiplatform FRP applications via the reflex haskell
library [0].

After many years of wrangling FRP my biggest learned lesson is certainly this:
not everything is a stream! You need to be able to mix streams (also called
events), which embody push semantics and behaviors (things you can sample),
which embody pull semantics. This is important for performance and for the
structure of large applications.

Coming up with a set of primitives to efficiently combine events and behaviors
in expressive ways is really difficult but worth it.

[0] [https://github.com/reflex-frp/reflex-platform](https://github.com/reflex-
frp/reflex-platform)

~~~
naasking
> After many years of wrangling FRP my biggest learned lesson is certainly
> this: not everything is a stream! You need to be able to mix streams (also
> called events), which embody push semantics and behaviors (things you can
> sample), which embody pull semantics.

I have a creeping suspicion that after a few more years, "push" is just going
to be viewed as codata, and "pull" will just be "data". I mean, that's kind of
what they are, our languages just don't have codata as first-class citizens,
and so we shoehorn it into stream abstractions.

~~~
zachrose
Could you unpack this a bit for the relative layperson? I once heard a guy at
a Haskell meetup say that “codata is the categorical dual of data” but I
didn’t know what that means then and I still don’t know.

~~~
Retra
Data is finite data structures, while Codata is infinite data structures (like
streams.) They are duals because you can generate and process data using
induction, while you can use coinduction to generate and process codata. One
is "built up" naturally, and the other is "torn down" naturally.

------
hudbuddy
Since no one has mentioned it yet, and it respectably isn't mentioned in the
article, the author of this is the creator of the cycle.js (cycle.js.org).

It is a really cool way of looking at DOM state as a flow of: <interface> ->
user interaction -> application reaction -> <interface>

The general idea being that the state of an application can be represented by
a timeline of events throughout the lifetime of an application. Since it is
entirely time and event driven/stateless, it gives way to some pretty cool
"time travel" concepts.

The philosophy is this:

If you know how something begins, and you know the exact times at which
anything happens to it, then you can assume its state at any given period in
time.

~~~
jfoutz
IMHO, you run into the same problem as journaling. User interaction is
basically a stream of deltas. If everything is perfect, it’s awesome. But
really, you want checkpoints from time to time.

It’s a cool idea. It’s cooler if your organization has the chops to rarely
introduce defects. Replaying on refresh works ok ish. The really cool part is
making every interaction with the system uniform. State comes in, small tweak,
state goes out.

------
quadcore
While this may be technically sound, I think this is not the most fundamental
way of explaining FRP. FRP is just a toy really. It's a useful toy that help
to grasp and use an advanced programming concept. Let me explain.

That concept originates from a common programming mistake. A common mistake is
to use _compulsive caching_. It's the tendency to store function results in a
variable just because this result is gonna be re-used. The right way instead
is to _evaluate_ (or "re-evaluate") the function every time you need it unless
you have a good reason not to (the only good reason is that the function is a
bottleneck that affects the experience of the user). This is a better way of
doing things because as everybody knows, invalidating caches is a hard thing
to do (and variables, guess what, are just caches).

The added benefit (which really is not a benefit, but just good programming)
is that things gonna now seem _reactive_.

~~~
raquo
This "assignment is caching is bad" analogy does not hold any water.

You get a result from a call to `foo(bar)`. You have to _do something_ with
that result. One common thing you'd need to do is to call another function:
_baz(foo(bar))_. But then you're just hiding the assignment inside a baz
function call (the result of _foo(bar)_ will be available as a
variable/constant inside of _baz_).

That doesn't so bad if you only need to do one thing with with the result of
`foo(bar)`. Now what if you have to do something with that result twice. In
this case all you've done is reduce the scope of a constant to be smaller than
it needs to be, requiring you to make a duplicate `foo(bar)` call.

Now you're repeating yourself and you don't know anymore if you're
conceptually dealing with the same result or if foo(bar) might produce a
different result on second call. You removed philosophical/semantic meaning
from your code and did not gain anything from it.

If you find yourself wanting/needing to "expire" variables that you define
before you run out of scope, then the answer is a combination of:

1) use constants of immutable data structures, not variables or mutable
structures 2) better name such constants so that you know what they represent
3) better manage scope and function size

In practice, the above is much easier to achieve than what you're proposing,
and makes the code easier to read and maintain.

> The right way instead is to evaluate (or "re-evaluate") the function every
> time you need it unless you have a good reason not to (the only good reason
> is that the function is a bottleneck that affects the experience of the
> user)

That does not scale to using the coding paradigm that you're proposing
application-wide. Your whole app will be one big bottleneck because it's doing
a multiple of the computations that it needs to be doing, with no easy way
out. I guess you can get away with that by throwing more money at your backend
infra, but you can't do that on mobile or for web frontends or desktop apps.

~~~
naasking
> You get a result from a call to `foo(bar)`. You have to _do something_ with
> that result. One common thing you'd need to do is to call another function:
> _baz(foo(bar))_. But then you're just hiding the assignment inside a baz
> function call (the result of _foo(bar)_ will be available as a
> variable/constant inside of _baz_).

Unless the OP is arguing for call-by-name evaluation, which when you take his
words literally, sounds like exactly what he's arguing for.

~~~
raquo
Call-by-name is just syntactic sugar for anonymous functions. It has all the
same problems.

But more importantly, the whole premise is wrong. "Cache invalidation" for
data that has an average lifespan of 30 lines of code in a single function is
not a hard problem. Real cache invalidation is hard for reasons that don't
translate to the case of ephemeral constants with properly limited scope.

~~~
forkerenok
> Call-by-name is just syntactic sugar for anonymous functions.

No, it's not. It defines different evaluation semantics.

~~~
raquo
Which are exactly equivalent to a function call, just with a different type,
and a syntax without parens.

"this will be re-evaluated every time you access it"

~~~
forkerenok
I see what you meant now. Though it seems you are mixing notions of anonymous
function and function as an argument of higher order functions (it doesn't
have to be anonymous).

------
doublerebel
This is an interesting article, but I don't think it gives enough credit to
Functional Programming. The article presents functional programming applied to
push streams, which is part of FRP but only one way to look at Reactive
Programming.

Thinking in streams is a great exercise but is not a panacea. It's really easy
to get started with streams in a toy example, but in real world applications
backpressure and buffering are a concern. The consideration of backpressure
also opens the push/pull stream discussion. If you are interested in applying
more from this article, the API is very similar in Godot2 (Node) [0], which is
based on Riemann (Ruby) [1]. _(Node streams have configurable backpressure
/buffering [2].)_

Complex interconnected events can cause a runaway snowball effect if not
carefully constructed or bound by limits:

* Elm's "time-travel" debugger is so valuable because it allows introspection through an event chain that may not be linear.

* Node takes a common approach, which is to push data/events through the stream but handle errors by bubbling up the stream processing chain. This Node approach maintains a hierarchy that is relatively easy to reason about.

* React is popular because it effectively is FRP applied to the data model: the JSX view is the function, and the change in data model is the event. Since data model changes are limited, atomic, and one-way this makes React easier to reason about than other frameworks (Angular, I'm looking at you). This concept pre-dates React, I've used it since years ago with SpineJS and Knockout.

Last but not least, when using streams there is an important tradeoff between
speed/latency and delivery/processing guarantees, which is enough for its own
discussion...

[0]:
[https://github.com/nextorigin/godot2](https://github.com/nextorigin/godot2)

[1]: [http://riemann.io/concepts.html](http://riemann.io/concepts.html)

[2]: [https://nodejs.org/en/docs/guides/backpressuring-in-
streams/](https://nodejs.org/en/docs/guides/backpressuring-in-streams/)

~~~
seanmcdirmid
Most of those aren't FRP, at least in the Elliott/Hudak sense. Or if you mean
anything that pushes streams around is FRP, then of course that existed long
before FRP was even a word and happens in non-functional paradigms as well.

~~~
mpweiher
Voice of reason. Thank you!

Synchronous dataflow, Lucid, Esterel, Supercollider :-)

FP does not solve a problem for reactive programming, reactive programming
solves a problem for FP.

------
mlazos
This is great, and if anyone is looking for additional examples in a library
that is a bit different from the others check out Racket’s reactive
programming library.

docs.racket-lang.org/frtime

This was how I was introduced to reactive programming 5 or so years ago.

------
platz
previous discussion
[https://news.ycombinator.com/item?id=7964873](https://news.ycombinator.com/item?id=7964873)

There was a lot of talk on what FRP (Functional Reactive Programming) really
meant, and if Rx is really FRP.

My understanding these days is that Rx has distanced itself from the FRP term
proper, while retaining the goals of programming with reactive streams.

~~~
mpweiher
Well, Conal has also distanced his work from the FRP term.

~~~
platz
My point still stands.

------
tomelders
Ah, streams. Or as I like to call them, "mysterious voids of side-effect
riddled effervescent state".

------
johnsonjo
I really liked Kris Kowal’s general theory of reactivity [1]. It’s a pretty in
depth explanation of reactive programming in JavaScript that I found really
illuminating. It goes through many different kinds of asynchronous data types
and compares them to their synchronous counterparts along the way.

[1]:
[https://github.com/kriskowal/gtor/blob/master/README.md](https://github.com/kriskowal/gtor/blob/master/README.md)

------
bajablast
This is awesome! Thank you for writing this Buzzword/Jargon free tutorial it's
so much more helpful to have things explained in terms of concepts the reader
already knows.

Good stuff!

------
ak39
Can traditional languages like C, C++ and even Object Pascal support reactive
programming? Or this is only implementable in functional languages?

~~~
barbersturgeon
They absolutely can. Take a look at [http://reactivex.io](http://reactivex.io)
for implementations in lots of imperative/oo languages. _Functional_ reactive
programming can also be done in these languages but may lack some of the
convenince of a functional oriented language

------
Paradigma11
What should we call original FRP now since Stream/Flow based programming has
highjacked the term?

------
RobertRoberts
Pardon my blunt question, but who is the heck is this for? Is this a sideways
method of pushing React.js? Or is this for assembly programmers as well, and I
just don't get it?

~~~
azangru
It has nothing to do with React (in fact, React, despite its name, does not
represent the paradigm of reactive programming).

This is for javascript programmers who enjoy functional programming and want
an introduction into Rx.js library. It may also be of interest for programmers
in other languages (because rx has been implemented in many of them).

------
fnord77
> "this new thing called Reactive Programming"

Let's take an age-old paradigm and present it as something new... why, I don't
know, maybe so we can come across as an expert on something "new" and high-
tech for fame and fortune...

...at least until someone who has been around the block comes along and
figures out you're at best a bullshit artist and at worst completely ignorant
and unqualified to be presenting...

~~~
adamnemecek
Ok where did you write reactive back in the day?

~~~
mathgladiator
I don't think it was called reactive back in the day, but the ideas are old to
some degree. If you drop the functional aspects, then "Reactive" programming
goes back to Lotus 123 (for the those too young, think Excel).

Data base triggers is also "Reactive", and materialized views are also
"reactive".

Are there some novel aspects to "FRP", sure.

~~~
adamnemecek
And OS interrupts are also reactive?

~~~
TuringTest
Can you compose incoming OS interrupts?

------
JepZ
From what I've read so far it sounds like defining a set of transform
functions which are executed whenever data arrives for their input parameters,
creating output values.

Is it different?

------
rhapsodic
I'm going to go out on a limb here with a prediction. Reactive Programming
will not catch on in any big way. Maybe in 10 years I'll look foolish for
having said this, but I'm willing to take the chance.

~~~
seriousluoldmof
Back when I was a kid, we called this rapid application development - but then
again, we didn't create cults around dev methodologies...

~~~
naasking
> but then again, we didn't create cults around dev methodologies...

You sure about that? I certainly recall plenty of holy wars over object
oriented programming.

~~~
mhd
And the 4GL bubble.

------
madeuptempacct
Reactive is garbage: 1\. Has no point. Literally no reason for ovbservables
over promises/events. 2\. Very difficult to debug. 3\. All the "good" things,
like pure functions, are not exclusive to it.

