18

From ReactiveX introduction page:

It is sometimes called “functional reactive programming” but this is a misnomer. ReactiveX may be functional, and it may be reactive, but “functional reactive programming” is a different animal. One main point of difference is that functional reactive programming operates on values that change continuously over time, while ReactiveX operates on discrete values that are emitted over time.

Meanwhile, from Wikipedia's Functional Reactive Programming page, ReactiveX is listed in the "Implementations" section:

Implementations[edit]

  • cellx, Ultra-fast implementation of reactivity for javascript
  • Elm, FRP language that compiles to HTML, CSS, and JavaScript
  • Frappuccino FRP implementation in Ruby
  • Flapjax, behavior/event FRP implementation in JavaScript
  • Reactive.jl, FRP implementation in Julia
  • ReactiveX, FRP implementation in multiple languages, including Java, JavaScript, Python, Swift and many more
  • reactive-banana FRP implementation in Haskell
  • ReactiveCocoa FRP implemented in Swift and Objective-C
  • ReactiveKit FRP implemented in pure Swift
  • Reflex FRP implementation in Haskell
  • Scala.Rx FRP implementation in Scala (and Scala.js)
  • Sodium, FRP implementation in C#, C++, Haskell (deprecated[12]), Java, > Rust, and Scala
  • Yampa FRP implementation in Haskell

I quite understand what ReactiveX does, and also did some researches about "Reactive Programming" and "Functional Reactive Programming", but I still can't distinguish the relationships between them.

In fact, I kind of believe that Wikipedia page is a misnomer, or incorrectly listing the examples in the "Implementations" section since I know that cellx and ReactiveX (which is both listed in the examples) is built to solve completely different problems.

TrungDQ
  • 6,286
  • 3
  • 31
  • 54

2 Answers2

19

Author of the reactive-banana library here.

The key distinction between functional reactive programming (FRP) and reactive programming (RP) is that the former has a well-defined denotational semantics, usually obtained from the types

type Behavior a  ~=  Time -> a
type Event    a  ~=  [(Time, a)]

while the latter does not have a well-defined denotational semantics. In particular, all implementations of RX that I know suffer from the problem that merging event streams is non-deterministic: When the streams contain simultaneous events, then sometimes one occurrence is merged before the other, and sometimes the other way round.

Moreover, the statement that "FRP operates on values that change continuously over time" is both subtly incorrect and not the key difference:

  • First, the most probable parse for this statement is that "Behaviors are continuous functions Time -> a", which is not true: Behaviors can be discontinuous, for instance they can be step functions. What is true instead is that Time in FRP is usually taken to be a real number, i.e. a continuum of values.
  • Second, it is perfectly possible to have FRP where time is discrete. That is not the key difference to RP, instead it is all about whether your operations on values have a well-defined denotational semantics or not.
Heinrich Apfelmus
  • 11,039
  • 1
  • 35
  • 66
  • Hi, thank you for your response, can you explain those types you put in the code block? – TrungDQ Feb 29 '16 at 19:02
  • @TrungDQ Well, the type `Time -> a` denotes a function with one parameter of type `Time` and a result value of type `a`. It can be interpreted as a value that changes over time. The type `[(Time, a)]` denotes a list of pairs of values of type `Time` and values of type `a`. It can be interpreted as a set of event occurrences at happen at separate points in time. – Heinrich Apfelmus Mar 01 '16 at 09:50
6

As far as I understand, from the point of view of ReactiveX (aka RX), it is simply impossible to have two events happening at the same "time". These are just callbacks being fired internally, sequentially, in the order of subscription. RX does not "manage" time.

RX can act rather crazy as seen through the eyes of a pure FRP programmer. Consider the following RXJS code:

const xs = Rx.Observable
    .interval(0)
  //.share();

xs.combineLatest(xs, (a,b) => [a,b])
    .filter(ab => ab[1] > ab[0])
    .take(1)
    .subscribe(ab => alert(ab));

Here xs is a cold observable interval that fires as fast possible. Since xs.combineLatest(ys, f) always subscribes to xs first, then to ys, you would expect xs.combineLatest(xs, (a,b) => [a,b]) to produce [0,0], [1,0], [1,1], [2,1], ... So ab[1] > ab[0] should always be false. However, on my PC, if I keep this code running for a while, it does end at some point it could take a while, try it yourself

That's because xs is a cold observable: each subscription to interval will create an independently running periodic timer. These timers can and will at some point fire in a different order (especially on multi-threaded environments like .NET)

If we comment out the //share line, making xs hot, the sequence never completes, as [0,0], [1,0], [1,1], ... ,[i,i-1],[i,i]... is now deterministically generated. This is because a hot observable shares a single subscription. In this case only a single timer is created.

In a real FRP system, this behavior would be deterministic. However, if you would really hookup to different hardware timers in a real FRP system, you would also get the same behavior as RX, since these external events will fire in random order, unless the two timers are perfectly synchronized of course

Ziriax
  • 826
  • 9
  • 17