Из Википедии, бесплатной энциклопедии
Перейти к навигации Перейти к поиску

В вычислении , реактивная программирования является декларативной парадигмой программирования связаны с потоками данных и распространением изменений. С помощью этой парадигмы можно легко выразить статические (например, массивы) или динамические (например, источники событий) потоки данных , а также сообщить, что предполагаемая зависимость в связанной модели выполнения существует, что облегчает автоматическое распространение измененных данных. поток. [ необходима цитата ]

Например, в настройке императивного программирования это будет означать, что в момент оценки выражения ему присваивается результат , а позже значения и могут быть изменены без влияния на значение . С другой стороны, в реактивном программировании значение автоматически обновляется всякий раз, когда значения или меняются, без необходимости в программе явно повторно выполнять оператор, чтобы определить присвоенное в настоящее время значение [ необходима цитата ]

var b = 1var c = 2var a = b + cб = 10console.log (a) // 3 (не 12, потому что "=" не является оператором реактивного присваивания)// теперь представьте, что у вас есть специальный оператор "$ =", который изменяет значение переменной (выполняет код в правой части оператора и присваивает результат левой переменной) не только при явной инициализации, но и при ссылках на переменные ( в правой части оператора) измененыvar b = 1var c = 2var a $ = b + cб = 10console.log (a) // 12

Другой пример - язык описания оборудования, такой как Verilog , где реактивное программирование позволяет моделировать изменения по мере их распространения по схемам. [ необходима цитата ]

Реактивное программирование было предложено как способ упростить создание интерактивных пользовательских интерфейсов и системной анимации в режиме, близком к реальному времени. [ необходима цитата ]

Например, в архитектуре модель – представление – контроллер (MVC) реактивное программирование может способствовать изменениям в базовой модели , которые автоматически отражаются в связанном представлении . [1]

Подходы к созданию реактивных языков программирования [ править ]

При создании языков реактивного программирования используется несколько популярных подходов. Спецификация выделенных языков, специфичных для различных ограничений домена . Такие ограничения обычно характеризуются описанием встроенных вычислений или оборудования в реальном времени. Другой подход включает спецификацию языков общего назначения, которые включают поддержку реактивности. Другие подходы сформулированы в определении и использовании программных библиотек или встроенных предметно-ориентированных языков , которые обеспечивают реактивность наряду с языком программирования или поверх него. Спецификация и использование этих различных подходов приводит к компромиссу с языковыми возможностями. В общем, чем более ограничен язык, тем больше связанные с ним компиляторы и инструменты анализа могут информировать разработчиков (например, при выполнении анализа того, могут ли программы выполняться в реальном времени). Функциональные компромиссы в специфичности могут привести к ухудшению общей применимости языка.

Модели и семантика программирования [ править ]

Семейство реактивного программирования управляется множеством моделей и семантики. Мы можем условно разделить их по следующим размерам:

  • Синхронность: является ли лежащая в основе модель времени синхронной или асинхронной?
  • Детерминизм: детерминированный или недетерминированный как в процессе оценки, так и в результатах
  • Процесс обновления: обратные вызовы против потока данных против актера

Методы и проблемы реализации [ править ]

Суть реализаций [ править ]

Среда выполнения языка реактивного программирования представлена ​​графиком, который определяет зависимости между задействованными реактивными значениями. В таком графе узлы представляют собой процесс вычисления, а отношения зависимости модели ребер . Такая среда выполнения использует указанный граф, чтобы отслеживать различные вычисления, которые должны выполняться заново, как только задействованный ввод изменяет значение.

Изменить алгоритмы распространения [ править ]

Наиболее распространенные подходы к распространению данных:

  • Вытягивание : Потребитель значения на самом деле является проактивным , поскольку он регулярно запрашивает значения у наблюдаемого источника и реагирует всякий раз, когда релевантное значение доступно. Такая практика регулярной проверки событий или изменений значений обычно называется опросом .
  • Push : потребитель значения получает значение из источника всякий раз, когда значение становится доступным. Эти значения являются самодостаточными, например, они содержат всю необходимую информацию, и потребителю не требуется запрашивать дополнительную информацию.
  • Push-pull : Потребитель значения получает уведомление об изменении , которое является кратким описанием изменения, например, «какое-то значение изменено» - это часть push . Однако уведомление не содержит всей необходимой информации (т.е. не содержит фактических значений), поэтому потребителю необходимо запросить у источника дополнительную информацию (конкретное значение) после того, как он получит уведомление - это извлечениечасть. Этот метод обычно используется, когда существует большой объем данных, которые могут быть потенциально интересны потребителям. Таким образом, для уменьшения пропускной способности и задержки отправляются только легкие уведомления; а затем те потребители, которым требуется дополнительная информация, будут запрашивать эту конкретную информацию. У этого подхода также есть недостаток, заключающийся в том, что источник может быть перегружен множеством запросов на дополнительную информацию после отправки уведомления.

Что толкать? [ редактировать ]

На уровне реализации реакция на событие состоит из распространения информации на графике, которая характеризует наличие изменения. Следовательно, вычисления, на которые влияет такое изменение, затем становятся устаревшими и должны быть помечены для повторного выполнения. Такие вычисления обычно характеризуются переходным закрытием изменения в соответствующем источнике. Распространение изменений может затем привести к обновлению значения стоков графа .

Информация, распространяемая графом, может состоять из полного состояния узла, т. Е. Результата вычислений задействованного узла. В таких случаях предыдущий вывод узла игнорируется. Другой метод включает в себя дельта-распространение, т.е. распространение инкрементных изменений . В этом случае информация распространяется по ребрам графа , которые состоят только из дельт, описывающих, как был изменен предыдущий узел. Этот подход особенно важен, когда узлы содержат большие объемы данных о состоянии , которые в противном случае было бы дорого пересчитывать с нуля.

Дельта-распространение - это, по сути, оптимизация, которая широко изучалась в дисциплине инкрементных вычислений , подход которой требует удовлетворения во время выполнения, включая проблему обновления представления . Эта проблема, как известно, характеризуется использованием сущностей базы данных , которые отвечают за поддержку изменяющихся представлений данных.

Другой распространенной оптимизацией является использование унарного накопления изменений и пакетного распространения . Такое решение может быть быстрее, так как сокращает обмен данными между задействованными узлами. Затем можно использовать стратегии оптимизации, которые определяют характер изменений, содержащихся внутри, и вносят соответствующие изменения. например, два изменения в пакете могут аннулировать друг друга и, таким образом, просто игнорироваться. Еще один доступный подход описан как распространение уведомления о недействительности . Этот подход заставляет узлы с недопустимыми входными данными извлекать обновления, что приводит к обновлению их собственных выходных данных.

Есть два основных способа построения графа зависимостей :

  1. График зависимостей поддерживается неявно внутри цикла событий . Регистрация явных обратных вызовов приводит к созданию неявных зависимостей. Следовательно, инверсия управления , которая вызывается обратным вызовом, остается на месте. Однако для того, чтобы сделать обратные вызовы функциональными (то есть возвращать значение состояния вместо значения единицы), необходимо, чтобы такие обратные вызовы стали композиционными.
  2. График зависимостей зависит от программы и создается программистом. Это облегчает адресацию инверсии управления обратным вызовом двумя способами: либо граф указывается явно (обычно с использованием предметно-ориентированного языка (DSL), который может быть встроен), либо граф неявно определяется с выражением и генерацией с использованием эффективного , архетипический язык .

Проблемы реализации в реактивном программировании [ править ]

Глюки [ править ]

При распространении изменений можно выбрать такие порядки распространения, чтобы значение выражения не было естественным следствием исходной программы. Мы можем легко проиллюстрировать это на примере. Предположим seconds, это реактивное значение, которое изменяется каждую секунду, чтобы представить текущее время (в секундах). Рассмотрим это выражение:

t = секунды + 1g = (t> секунд)

Поскольку tвсегда должно быть больше seconds, это выражение всегда должно возвращать истинное значение. К сожалению, это может зависеть от порядка оценки. При secondsизменении должны обновляться два выражения: seconds + 1и условное. Если первый выполняется раньше второго, то этот инвариант сохраняется. Если, однако, сначала обновляется условие, используя старое значение tи новое значение seconds, тогда выражение будет оцениваться как ложное значение. Это называется глюк .

Некоторые реактивные языки не содержат сбоев и подтверждают это свойство [ необходима ссылка ] . Обычно это достигается топологической сортировкой выражений и обновлением значений в топологическом порядке. Однако это может повлиять на производительность, например задержку доставки значений (из-за порядка распространения). Поэтому в некоторых случаях реактивные языки допускают сбои, и разработчики должны знать о возможности того, что значения могут временно не соответствовать исходному тексту программы и что некоторые выражения могут оцениваться несколько раз (например, t > secondsмогут оцениваться дважды: один раз, когда secondsприбывает новое значение и еще раз при tобновлении).

Циклические зависимости [ править ]

Топологическая сортировка зависимостей зависит от того, является ли граф зависимостей направленным ациклическим графом (DAG). На практике программа может определять граф зависимостей с циклами. Обычно языки реактивного программирования ожидают, что такие циклы будут «прерваны» путем размещения некоторого элемента вдоль «задней кромки», чтобы разрешить завершение реактивного обновления. Обычно языки предоставляют такой оператор, delayкоторый используется механизмом обновления для этой цели, поскольку a delayозначает, что то, что следует за ним, должно быть оценено на «следующем временном шаге» (позволяющем завершить текущую оценку).

Взаимодействие с изменяемым состоянием [ править ]

Реактивные языки обычно предполагают, что их выражения являются чисто функциональными . Это позволяет механизму обновления выбирать разные порядки для выполнения обновлений и оставлять конкретный порядок неуказанным (тем самым обеспечивая оптимизацию). Однако, когда реактивный язык встроен в язык программирования с состоянием, программисты могут выполнять изменяемые операции. Как сделать это взаимодействие гладким, остается открытой проблемой.

В некоторых случаях возможны принципиальные частичные решения. Два таких решения включают:

  • В языке может быть понятие «изменчивой ячейки». Изменяемая ячейка - это ячейка, о которой знает система реактивного обновления, поэтому изменения, внесенные в ячейку, распространяются на остальную часть реактивной программы. Это позволяет нереактивной части программы выполнять традиционную мутацию, в то же время позволяя реактивному коду знать об этом обновлении и реагировать на него, таким образом поддерживая согласованность отношений между значениями в программе. Примером реактивного языка, который предоставляет такую ​​ячейку, является FrTime. [2]
  • Правильно инкапсулированные объектно-ориентированные библиотеки предлагают инкапсулированное понятие состояния. В принципе, поэтому такая библиотека может беспрепятственно взаимодействовать с реактивной частью языка. Например, обратные вызовы могут быть установлены в геттерах объектно-ориентированной библиотеки для уведомления механизма реактивного обновления об изменениях состояния, а изменения в реактивном компоненте могут быть отправлены в объектно-ориентированную библиотеку через геттеры. FrTime использует такую ​​стратегию. [3]

Динамическое обновление графика зависимостей [ править ]

В некоторых реактивных языках граф зависимостей статичен , т. Е. Граф фиксируется на протяжении всего выполнения программы. В других языках граф может быть динамическим , т. Е. Он может изменяться по мере выполнения программы. В качестве простого примера рассмотрим этот иллюстративный пример (где seconds- реактивное значение):

t = if ((секунд по модулю 2) == 0): секунды + 1 еще: секунды - 1 конецт + 1

Каждую секунду значение этого выражения меняется на другое реактивное выражение, которое t + 1затем зависит от. Поэтому график зависимостей обновляется каждую секунду.

Разрешение динамического обновления зависимостей обеспечивает значительную выразительную мощь (например, динамические зависимости обычно возникают в программах с графическим пользовательским интерфейсом (GUI)). Однако механизм реактивного обновления должен решить, восстанавливать ли выражения каждый раз или сохранять узел выражения созданным, но неактивным; в последнем случае убедитесь, что они не участвуют в вычислениях, когда они не должны быть активными.

Концепции [ править ]

Степени явности [ править ]

Реактивные языки программирования могут варьироваться от очень явных, в которых потоки данных настраиваются с помощью стрелок, до неявных, когда потоки данных являются производными от языковых конструкций, похожих на конструкции императивного или функционального программирования. Например, в неявно поднятом функциональном реактивном программировании (FRP) вызов функции может неявно вызывать создание узла в графе потока данных. Библиотеки реактивного программирования для динамических языков (например, библиотеки Lisp "Cells" и Python "Trellis") могут создавать граф зависимостей на основе анализа значений, считываемых во время выполнения функции, во время выполнения, что позволяет спецификациям потока данных быть как неявными, так и динамическими.

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

Статический или динамический [ править ]

Реактивное программирование может быть чисто статическим, когда потоки данных настроены статически, или динамическим, когда потоки данных могут изменяться во время выполнения программы.

Использование переключателей данных в графе потока данных может в некоторой степени сделать статический граф потока данных динамичным и слегка размыть различия. Однако истинное динамическое реактивное программирование может использовать императивное программирование для восстановления графа потока данных.

Реактивное программирование высшего порядка [ править ]

Можно сказать, что реактивное программирование относится к более высокому порядку, если оно поддерживает идею о том, что потоки данных могут использоваться для создания других потоков данных. То есть результирующее значение из потока данных представляет собой другой граф потока данных, который выполняется с использованием той же модели оценки, что и первый.

Дифференциация потока данных [ править ]

В идеале все изменения данных распространяются мгновенно, но на практике этого нельзя гарантировать. Вместо этого может потребоваться дать разным частям графа потока данных разные приоритеты оценки. Это можно назвать дифференцированным реактивным программированием . [4]

Например, в текстовом процессоре маркировка орфографических ошибок не обязательно должна полностью синхронизироваться с вставкой символов. Здесь потенциально можно использовать дифференцированное реактивное программирование, чтобы дать программе проверки орфографии более низкий приоритет, что позволяет отложить ее выполнение, сохраняя при этом другие потоки данных мгновенными.

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

Оценочные модели реактивного программирования [ править ]

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

Было бы проблематично просто наивно распространять изменение с помощью стека из-за потенциальной экспоненциальной сложности обновления, если структура данных имеет определенную форму. Одна такая форма может быть описана как «повторяющаяся форма ромба» и имеет следующую структуру: A n → B n → A n + 1 , A n → C n → A n + 1 , где n = 1,2 ... Эту проблему можно решить, распространяя аннулирование только в том случае, если некоторые данные еще не признаны недействительными, а затем повторно проверяйте данные, когда это необходимо, с помощью ленивого вычисления .

Одна из неотъемлемых проблем реактивного программирования состоит в том, что большинство вычислений, которые были бы оценены и забыты на нормальном языке программирования, должны быть представлены в памяти как структуры данных. [ необходима цитата ] Это может потенциально сделать реактивное программирование очень потребляющим память. Однако исследования того, что называется понижением, потенциально могут решить эту проблему. [5]

С другой стороны, реактивное программирование - это форма того, что можно описать как «явный параллелизм» [ цитата необходима ] , и поэтому оно может быть полезным для использования мощности параллельного оборудования.

Сходства с паттерном наблюдателя [ править ]

Реактивное программирование имеет принципиальное сходство с шаблоном наблюдателя, обычно используемым в объектно-ориентированном программировании . Однако интеграция концепций потока данных в язык программирования упростит их выражение и, следовательно, может повысить степень детализации графа потока данных. Например, шаблон наблюдателя обычно описывает потоки данных между целыми объектами / классами, тогда как объектно-ориентированное реактивное программирование может нацеливаться на члены объектов / классов.

Подходы [ править ]

Императив [ править ]

Можно объединить реактивное программирование с обычным императивным программированием. В такой парадигме императивные программы оперируют реактивными структурами данных. [6] Такая установка аналогична императивному программированию с ограничениями ; однако, в то время как императивное программирование с ограничениями управляет двунаправленными ограничениями, реактивное императивное программирование управляет односторонними ограничениями потока данных.

Объектно-ориентированный [ править ]

Объектно-ориентированное реактивное программирование (OORP) - это комбинация объектно-ориентированного программирования и реактивного программирования. Возможно, наиболее естественный способ создать такую ​​комбинацию заключается в следующем: вместо методов и полей у объектов есть реакции, которые автоматически переоцениваются, когда другие реакции, от которых они зависят, были изменены. [ необходима цитата ]

Если язык OORP поддерживает свои императивные методы, он также попадает в категорию императивного реактивного программирования.

Функциональный [ править ]

Функциональное реактивное программирование (FRP) - это парадигма программирования для реактивного программирования функционального программирования .

На основе правил [ править ]

Относительно новая категория языков программирования использует ограничения (правила) в качестве основной концепции программирования. Он состоит из реакций на события, при которых соблюдаются все ограничения. Это не только облегчает реакции, основанные на событиях, но и делает реактивные программы инструментом правильности программного обеспечения. Примером языка реактивного программирования, основанного на правилах, является Ampersand, основанный на алгебре отношений .[7]

Реализации [ править ]

  • ReactiveX , API для реализации реактивного программирования с потоками, наблюдаемыми объектами и операторами с множеством языковых реализаций, включая RxJs, RxJava, .NET, RxPy и RxSwift.
  • Elm (язык программирования) Реактивная композиция веб-интерфейса пользователя.
  • Reactive Streams , стандарт JVM для асинхронной обработки потоков с неблокирующим противодавлением
  • ObservableComputations , кроссплатформенная реализация .NET.

См. Также [ править ]

  • Наблюдаемый (вычисления) , наблюдаемый в реактивном программировании.

Ссылки [ править ]

  1. ^ Trellis, Модель-представление-контроллер и шаблон наблюдателя , сообщество Tele.
  2. ^ «Встраивание динамического потока данных в язык вызова по значению» . cs.brown.edu . Проверено 9 октября 2016 .
  3. ^ «Пересечение границ состояния: адаптация объектно-ориентированных фреймворков к функциональным реактивным языкам» . cs.brown.edu . Проверено 9 октября 2016 .
  4. ^ «Реактивное программирование - Искусство обслуживания | Руководство по управлению ИТ» . theartofservice.com . Проверено 2 июля 2016 .
  5. ^ Берчетт, Кимберли; Купер, Грегори Х; Кришнамурти, Шрирам, «Понижение: метод статической оптимизации для прозрачной функциональной реактивности», Труды симпозиума ACM SIGPLAN 2007 г. по частичной оценке и манипулированию программами на основе семантики (PDF) , стр. 71–80 .
  6. ^ Деметреску, Камил; Финокки, Ирэн; Рибичини, Андреа, «Реактивное императивное программирование с ограничениями потока данных», Труды международной конференции ACM 2011 года по языкам и приложениям объектно-ориентированных систем программирования , стр. 407–26.
  7. ^ Йоостен, Стеф (2018), «Связь Алгебра , как языка программирования , с помощью компилятора амперсанд», журнал логических и алгебраические методы в программировании , 100 , стр 113-29,. Дои : 10.1016 / j.jlamp.2018.04.002.

Внешние ссылки [ править ]

  • Обзор реактивного программирования Статья Э. Байномугиши, А. Ломбида Карретона, Т. Ван Катсем, С. Мостинкса и У. Де Мойтера, опубликованная в 2013 году, представляет собой обзор и систематизацию существующих подходов к реактивному программированию.
  • MIMOSA Проект INRIA - ENSMP , общий сайт о реактивном программировании.
  • Эксперименты с Cells Демонстрация простого приложения реактивного программирования на Lisp с использованием библиотеки Cells
  • REScala Реактивное программирование для объектно- ориентированных приложений.
  • Прекращение поддержки шаблона Observer Статья Инго Майера, Тиарка Ромпфа и Мартина Одерски , выпущенная в 2010 году, в общих чертах описывает структуру реактивного программирования для языка программирования Scala .
  • Устарение паттерна наблюдателя в Scala.React Статья Инго, 2012 г.
  • RxJS , библиотека Reactive Extensions для «составления асинхронных [...] программ с использованием наблюдаемых последовательностей»