ReactiveX считается реактивным программированием?

Со страницы введения ReactiveX:

Иногда его называют "функционально-реактивным программированием", но это неправильное название. ReactiveX может быть функциональным и реактивным, но "функциональное реактивное программирование" - это другое животное. Одно из главных отличий состоит в том, что функционально-реактивное программирование работает со значениями, которые постоянно изменяются во времени, тогда как ReactiveX работает с дискретными значениями, которые излучаются во времени.

Между тем, со страницы функционального реактивного программирования Википедии, ReactiveX указан в разделе "Реализации":

Implementations[edit]

  • cellx, Сверхбыстрая реализация реактивности для javascript
  • Elm, язык FRP, который компилируется в HTML, CSS и JavaScript
  • Реализация FRP Frappuccino в Ruby
  • Flapjax, реализация FRP поведения/события в JavaScript
  • Reactive.jl, реализация FRP в Юлии
  • ReactiveX, реализация FRP на нескольких языках, включая Java, JavaScript, Python, Swift и многие другие
  • Реализация реактивного бананового FRP в Haskell
  • FRP ReactiveCocoa реализован в Swift и Objective-C
  • ReactiveKit FRP реализован в чистом Swift
  • Рефлексная реализация FRP в Haskell
  • Реализация Scala.Rx FRP в Scala (и Scala.js)
  • Sodium, реализация FRP в С#, C++, Haskell (устарело [12]), Java,> Rust и Scala
  • Реализация FRP в Yampa на Хаскеле

Я вполне понимаю, что делает ReactiveX, а также провел некоторые исследования о "Реактивном программировании" и "Функциональном реактивном программировании", но я все еще не могу различить отношения между ними.

На самом деле, я вроде бы считаю, что страница Википедии является неправильным или неправильно перечисляет примеры в разделе "Реализации", так как я знаю, что cellx и ReactiveX (оба из которых перечислены в примеры) построен для решения совершенно разных задач.

Ответы

Ответ 1

Автор библиотеки реактивно-бананов.

Основное различие между функциональным реактивным программированием (FRP) и реактивным программированием (RP) состоит в том, что первая имеет четко определенную денотационную семантику, обычно получаемую из типов

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

в то время как последний не имеет четко определенной денотационной семантики. В частности, все реализации RX, которые, как я знаю, страдают от проблемы, что объединение потоков событий не является детерминированным: когда потоки содержат одновременные события, иногда иногда одно из них сливается перед другим, а иногда и наоборот.

Кроме того, утверждение о том, что "FRP работает со значениями, которые изменяются непрерывно во времени", является как тонко неправильным, а не ключевым различием:

  • Во-первых, наиболее вероятный анализ этого утверждения состоит в том, что "Поведение - это непрерывные функции Time -> a", что неверно: поведение может быть прерывистым, например, они могут быть ступенчатыми. Вместо этого следует, что Time в FRP обычно принимается как действительное число, т.е. Континуум значений.
  • Во-вторых, вполне возможно иметь FRP, где время дискретно. Это не ключевое отличие от RP, а все дело в том, что ваши операции над значениями имеют четко определенную денотационную семантику или нет.

Ответ 2

Насколько я понимаю, с точки зрения ReactiveX (он же RX) просто невозможно, чтобы два события происходили в одно и то же "время". Это просто обратные вызовы, запускаемые внутри, последовательно, в порядке подписки. RX не "управляет" временем.

RX может действовать довольно сумасшедшим, если смотреть глазами программиста на FRP. Рассмотрим следующий код RXJS:

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

Здесь xs - холодный наблюдаемый интервал, который срабатывает максимально быстро. Поскольку xs.combineLatest(ys, f) всегда подписывается сначала на xs, а затем на ys, можно ожидать, что xs.combineLatest(xs, (a,b) => [a,b]) выдаст [0,0], [1,0], [1,1], [2,1], ..., поэтому ab[1] > ab[0] всегда должно быть ложным. Тем не менее, на моем ПК, если я продолжаю выполнять этот код некоторое время, он в какой-то момент заканчивается, это может занять некоторое время, попробуйте сами

.Это потому, что xs является холодной наблюдаемой: каждая подписка на interval будет создавать независимо работающий периодический таймер. Эти таймеры могут и будут в какой-то момент срабатывать в другом порядке (особенно в многопоточных средах, таких как .NET)

Если мы закомментируем строку //share, сделав xs горячим, последовательность никогда не завершится, поскольку теперь [0,0], [1,0], [1,1], ... ,[i,i-1],[i,i]... генерируется детерминистически. Это потому, что горячая наблюдаемая разделяет одну подписку. В этом случае создается только один таймер.

В реальной системе FRP такое поведение будет детерминированным. Однако, если вы действительно подключитесь к разным аппаратным таймерам в реальной системе FRP, вы также получите то же поведение, что и RX, поскольку эти внешние события будут срабатывать в случайном порядке, если, конечно, два таймера не будут идеально синхронизированы