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, поскольку эти внешние события будут срабатывать в случайном порядке, если, конечно, два таймера не будут идеально синхронизированы