Что такое (функциональное) реактивное программирование?

Я прочитал статью в Википедии о реактивном программировании. Я также прочитал небольшую статью о функциональном реактивном программировании. Описание довольно абстрактное.

  1. Что означает функциональное реактивное программирование (FRP) на практике?
  2. Из чего состоит реактивное программирование (в отличие от нереактивного программирования?)?

Мой опыт работы с императивными / объектно-ориентированными языками, поэтому я буду благодарен за объяснение, относящееся к этой парадигме.


person JtR    schedule 22.06.2009    source источник
comment
вот парень с активным воображением и хорошими навыками повествования возьмет на себя все это. paulstovell.com/reactive-programming   -  person melaos    schedule 05.01.2010
comment
Различные виды семантики можно найти в Максимальная выразительная сила при минимальной конструкции. Здесь нет упоминания о реактивном программировании, за исключением того, что все программные символы, вызывающие другие символы, активируют свои подфункции в своих автоматах и ​​после завершения возвращают свой результат, если таковой имеется. Это простой феномен «вызов-возврат», который я тоже считаю реактивным программированием.   -  person Erkki    schedule 28.05.2010
comment
Кому-то действительно нужно написать Функциональное реактивное программирование для чайников для всех нас, самоучки. Все ресурсы, которые я нашел, даже Эльм, похоже, предполагают, что вы получили степень магистра в области CS за последние пять лет. Те, кто разбирается в FRP, похоже, полностью потеряли способность смотреть на этот вопрос с наивной точки зрения, что имеет решающее значение для обучения, обучения и евангелизации.   -  person TechZen    schedule 04.05.2014
comment
См. Также stackoverflow.com/a/1028642/632951   -  person Pacerier    schedule 13.06.2014
comment
Еще одно отличное введение в FRP: Введение в реактивное программирование, которое вам не хватало моего коллеги Андре   -  person Jonik    schedule 03.07.2014
comment
Один из лучших, что я когда-либо видел, основан на примере: gist.github.com/staltz/868e7e9bc2a7b8c1f754   -  person Razmig    schedule 01.08.2014
comment
FRP для императивного программирования - это то же самое, что относительность (4-мерное пространство-время) для классической механики (3-мерное пространство + время) :)   -  person Vicky Chijwani    schedule 25.11.2014
comment
Я считаю аналогию с электронной таблицей очень полезной в качестве первого приблизительного впечатления (см. Ответ Боба: stackoverflow.com/a/1033066/1593924 ). Ячейка электронной таблицы реагирует на изменения в других ячейках (вытягивает), но не протягивает руку и не изменяет другие (не выталкивает). Конечным результатом является то, что вы можете изменить одну ячейку, а миллионы других «независимо» обновят свои собственные дисплеи.   -  person Jon Coombs    schedule 17.05.2015


Ответы (18)


Если вы хотите почувствовать FRP, вы можете начать со старого учебника Fran 1998 года, с анимированными иллюстрациями. Для статей начните с Функциональная реактивная анимация, а затем следуйте ссылкам на ссылка на публикации на моей домашней странице и ссылка FRP на Haskell wiki.

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

Я согласен с простым описанием Лоуренса Джи, что FRP - это «типы данных, которые представляют значение« с течением времени »». Обычное императивное программирование улавливает эти динамические значения только косвенно, через состояние и мутации. Полная история (прошлое, настоящее, будущее) не имеет первоклассного представления. Более того, только дискретно развивающиеся значения могут быть (косвенно) захвачены, поскольку императивная парадигма дискретна во времени. Напротив, FRP фиксирует эти изменяющиеся значения напрямую и не имеет проблем с непрерывно изменяющимися значениями.

FRP также необычен тем, что он является параллельным, не вступая в противоречие с теоретическим и прагматическим крысиным гнездом, которое преследует императивный параллелизм. Семантически параллелизм FRP бывает мелкозернистым, определенным и непрерывным. (Я говорю о значении, а не о реализации. Реализация может включать или не включать параллелизм или параллелизм.) Семантическая определенность очень важна для рассуждений, как строгих, так и неформальных. Хотя параллелизм значительно усложняет императивное программирование (из-за недетерминированного чередования), в FRP он не требует усилий.

Итак, что такое FRP? Вы могли бы это сами придумать. Начните с этих идей:

  • Динамические / развивающиеся ценности (т. Е. Значения «с течением времени») сами по себе являются ценностями первого класса. Вы можете определять их и комбинировать, передавать в функции и из них. Я назвал это «поведением».

  • Поведение строится из нескольких примитивов, таких как постоянное (статическое) поведение и время (например, часы), а затем из последовательной и параллельной комбинации. n поведения комбинируются путем применения n-арной функции (к статическим значениям) «по точкам», то есть непрерывно во времени.

  • Чтобы объяснить дискретные явления, используйте другой тип (семейство) «событий», каждое из которых имеет поток (конечный или бесконечный) событий. Каждому событию соответствует время и значение.

  • Чтобы придумать композиционный словарь, из которого можно построить все поведения и события, поиграйте с некоторыми примерами. Продолжайте разбирать на более общие / простые части.

  • Чтобы вы знали, что стоите на твердой почве, дайте всей модели композиционную основу, используя технику денотационной семантики, которая просто означает, что (а) каждый тип имеет соответствующий простой и точный математический тип «значений» и ( б) каждый примитив и оператор имеют простое и точное значение в зависимости от значений составляющих. Никогда и никогда не смешивайте вопросы реализации в процессе исследования. Если это описание кажется вам тарабарщиной, обратитесь к (а) Денотационный дизайн с морфизмами классов типов, (b) Функциональное реактивное программирование методом push-pull (игнорируя биты реализации) и (c) денотационная семантика Страница викиучебников Haskell. Помните, что денотационная семантика состоит из двух частей, от двух ее основателей Кристофера Стрейчи и Даны Скотт: более легкая и полезная часть Стрейчи и более сложная и менее полезная (для разработки программного обеспечения) часть Скотта.

Если вы будете придерживаться этих принципов, я надеюсь, вы получите что-то более или менее в духе FRP.

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

Было довольно сложно реализовать эту модель правильно и эффективно, но это уже другая история.

person Conal    schedule 23.06.2009
comment
Я знал о функциональном реактивном программировании. Кажется, это связано с моими собственными исследованиями (в интерактивной статистической графике), и я уверен, что многие из идей будут полезны для моей работы. Однако мне очень трудно пройти мимо языка - должен ли я действительно изучать денотационную семантику и морфизмы классов типов, чтобы понять, что происходит? Было бы очень полезно познакомить аудиторию с этой темой. - person hadley; 24.06.2009
comment
@Conal: вы четко понимаете, о чем говорите, но ваш язык предполагает, что у меня есть докторская степень по вычислительной математике, а у меня нет. У меня есть опыт работы в системной инженерии и более 20 лет опыта работы с компьютерами и языками программирования, но я все же чувствую, что ваш ответ меня озадачивает. Я призываю вас опубликовать свой ответ на английском языке ;-) - person mindplay.dk; 27.10.2011
comment
@ minplay.dk: Ваши замечания не дают мне повода говорить о том, чего вы, в частности, не понимаете, и я не склонен делать дикие предположения о том, какое именно подмножество английского языка вы ищете. Тем не менее, я предлагаю вам конкретно сказать, по каким аспектам моего объяснения выше вы спотыкаетесь, чтобы я и другие могли вам помочь. Например, есть ли определенные слова, которые вы хотели бы определить, или концепции, для которых вы бы хотели добавить ссылки? Мне действительно нравится улучшать ясность и доступность моего письма - не приукрашивая его. - person Conal; 26.12.2011
comment
Я думаю, что есть много незнакомых терминов, которые следует кратко объяснить. Например, что означает детерминированная и семантическая детерминированность с точки зрения параллелизма? Композиционная лексика? Если бы вы объяснили все эти новые термины, как вы это сделали с «денотационной семантикой», я думаю, что это было бы гораздо менее глупо для чтения. - person zenna; 11.02.2012
comment
Определенность / определенность означает, что существует одно четко определенное правильное значение. Напротив, почти все формы императивного параллелизма могут давать разные ответы, в зависимости от планировщика, от того, смотрите вы или нет, и они могут даже блокироваться. Семантический (а точнее денотационный) относится к значению (обозначению) выражения или представления, в отличие от операционного (как вычисляется ответ или сколько места и / или времени потребляется какой машиной). - person Conal; 11.02.2012
comment
Композиционный означает что-то составляющее, то есть построенное в стиле строительных блоков, как мы работаем с числами, используя арифметику, степени / корни, логарифмы, триггеры и т. Д. Словарь - это набор элементарных терминов и комбинирующих операторов / функций. - person Conal; 11.02.2012
comment
Я согласен с @ mindplay.dk, хотя не могу похвастаться тем, что проработал в этой области очень долго. Хотя казалось, что вы знаете, о чем говорите, это не дало мне быстрого, краткого и простого понимания того, что это такое, поскольку я достаточно избалован, чтобы ожидать от SO. Этот ответ в первую очередь побудил меня задать массу новых вопросов, но я не ответил на первый. Я надеюсь, что обмен опытом относительно невежества в этой области может дать вам представление о том, насколько простым и кратким вам нужно быть. Между прочим, я происхожу из того же происхождения, что и ОП. - person Aske B.; 27.06.2013
comment
Конечно, если вы были достаточно заинтересованы и у вас было несколько дней свободного времени, чтобы инвестировать только в изучение этого предмета, в нем есть хорошая глубина, но, как правило, более полезно получить простой ответ. Если меня интересуют другие подробности, я гуглю этот вопрос и, вероятно, найду другой SO-ответ, в котором была бы объяснена эта конкретная вещь, и, таким образом, все это идеально сочетается друг с другом. Это одна из причин, по которой я считаю, что простые и краткие примеры являются ключевыми для обучения / ответа. Надеюсь, это вам помогло. - person Aske B.; 27.06.2013
comment
@AskeB. Я думаю, проблема с этой концепцией в том, что она зависит от множества больших идей, которые математики точно определили в тех словах, значения которых вы не знаете. Итак, чтобы понять это, вам необходимо знать (по крайней мере, большинство) из упомянутых концепций. Если вы это сделаете, это (почти) тривиальное понимание концепции. Если вы этого не сделаете, вы точно не поймете сути, не изучив сначала упомянутые концепции. Так оно и есть. - person Profpatsch; 05.05.2014
comment
@AskeB. Вот в чем суть этих передовых идей. Вы можете понять идею, например, C через несколько часов, но вскоре появятся сложные проблемы. Чтобы их решить, вам нужны более продвинутые концепции. Это очень продвинутая и по-прежнему актуальная тема для исследований (с очень небольшим количеством реализаций и еще меньшим количеством программ, которые ее используют). - person Profpatsch; 05.05.2014
comment
@AskeB. Я мог бы спросить вас, усвоили ли вы такие вещи, как полиморфизм и наследование (которые, кстати, являются совершенно разными понятиями), и что они не зависят от объектно-ориентированного подхода и т. Д. Они представляют / сущность / вещей, как и вышеприведенный ответ. Такие вещи, как Java, C ++, Haskell, - это просто реализация идей. И вам не обязательно понимать концепции, чтобы использовать реализации. Разработчик также не обязан (к сожалению). Я бы сказал, что многие программисты этого не делают. - person Profpatsch; 05.05.2014
comment
Вот «упрощенная» интерпретация последнего абзаца Конала: Коналу нравится декларативный стиль программирования, который является следствием определения синтаксиса языка, основанного на «значениях», а не на последовательностях инструкций. Императивное программирование подходило для обучения, например, фон Неймана (с четким различием между ЦП и ОЗУ), а не для описания изменений в ЗНАЧЕНИЯх, которые нас действительно волнуют (назовем эти «функции»). то есть, если вы спросите функционального программиста и императивного программиста, что такое «переменная», вы получите совершенно разные ответы. - person fatuhoku; 20.05.2014
comment
FRP - это концепция выражения поведения программы в виде математической функции, которая сопоставляет значения Time с выходными значениями (будь то графика, музыка или что-то еще). Самый простой пример: очень примитивный видеопроигрыватель - это программа, которая может быть выражена как функция, отображающая миллисекунды на изображение, которое должно отображаться НА ЭТОМ МИЛЛИСЕКУНДЕ. например в синтаксисе Java что-то вроде public Picture frameForTime (float millisOffsetFromStartOfVideo) {// Большое декодирование из видеофайла ... return frame; } НО ФУНКЦИЯ НЕ ДОЛЖНА ИМЕТЬ ПОБОЧНЫХ ЭФФЕКТОВ. - person fatuhoku; 20.05.2014
comment
И да, программы FRP, к сожалению, тоже должны выполняться на реальных компьютерах. Таким образом, возникают некоторые проблемы, когда дело доходит до производительности или использования другой части прекрасной аппаратной архитектуры, доступной для вашей программы (например, DSP или GPGPU), но это не имеет значения, поскольку это зависит от реализации. - person fatuhoku; 20.05.2014
comment
Совпадает ли концепция FRP с концепцией реактивных приложений, описанной в реактивном манифесте? Или это просто досадное столкновение терминологии? - person Jus12; 08.07.2014
comment
Просто конфликт, они нигде не упоминают функции, зависящие от времени, не так ли? - person jhegedus; 31.01.2015
comment
К сожалению, широкой аудитории понять даже труднее, чем ответ Конала! Когда что-то не интуитивно для меня, это может означать, что мне нужно работать усерднее, а не обвинять автора в том, что он не смог угодить мне, в частности. - person Elliot Cameron; 30.03.2015
comment
@ Jus12: Я думаю, что методикам реактивного манифеста не хватает как фундаментальных принципов FRP (точное обозначение и непрерывное время). FRP, возможно, оказал влияние (или нет), но оба исходят с очень разных точек зрения. - person Conal; 31.03.2015
comment
Я думаю, это удивительно, что вы можете сказать все это и оставить меня не более информированным, чем я был в начале. Сможете ли вы разбить эти возвышенные концепции на функциональный язык, чтобы читатель мог различить различные плюсы и минусы FRP, проблему, которую они намереваются решить, и их преимущества по сравнению с существующими шаблонами? - person Quibblesome; 14.11.2017
comment
Вы говорили за меня. :) - person Mandroid; 28.01.2018
comment
Тем не менее, у меня есть несколько лет опыта программирования на C ++. С таким большим количеством голосов, но все еще трудно понять, что такое FRP. Грустить... :( - person ; 23.03.2018

В чисто функциональном программировании нет побочных эффектов. Для многих типов программного обеспечения (например, для всего, что связано с взаимодействием с пользователем) на определенном уровне необходимы побочные эффекты.

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

Основная идея реактивного программирования заключается в том, что существуют определенные типы данных, которые представляют значение «с течением времени». Вычисления, которые включают эти изменяющиеся во времени значения, сами будут иметь значения, которые изменяются во времени.

Например, вы можете представить координаты мыши как пару целочисленных значений во времени. Допустим, у нас было что-то вроде (это псевдокод):

x = <mouse-x>;
y = <mouse-y>;

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

Если мы затем сделаем некоторые вычисления на основе этого, результирующие значения также будут значениями, которые меняются со временем. Например:

minX = x - 16;
minY = y - 16;
maxX = x + 16;
maxY = y + 16;

В этом примере minX всегда будет на 16 меньше, чем координата x указателя мыши. С реактивно-ориентированными библиотеками вы могли бы сказать что-то вроде:

rectangle(minX, minY, maxX, maxY)

Окно размером 32x32 будет нарисовано вокруг указателя мыши и будет отслеживать его, куда бы он ни двигался.

Вот довольно хорошая статья по функциональному реактивному программированию < / а>.

person Laurence Gonsalves    schedule 22.06.2009
comment
Значит, реактивное программирование - это форма декларативного программирования? - person troelskn; 23.06.2009
comment
d.drawRectangle (minX, minY, maxX, maxY); Кроме того: функциональное поведение прекрасно сочетается с функциональной графикой: rectangle(minX, minY, maxX, maxY), которая была бы выражением, а не утверждением. - person Conal; 23.06.2009
comment
›Значит, реактивное программирование - это форма декларативного программирования? Функциональное реактивное программирование - это форма функционального программирования, которая является формой декларативного программирования. - person Conal; 23.06.2009
comment
@Conal: Верно, функциональная графика имеет больше смысла. Я не уверен, почему я использовал императивный стиль для рисования прямоугольника. - person Laurence Gonsalves; 08.06.2010
comment
Так это как если бы Вы написали #define x mouse_x() на C? - person user712092; 25.05.2012
comment
@ user712092 Не совсем, нет. Например, если я вызываю sqrt(x) в C с помощью вашего макроса, он просто вычисляет sqrt(mouse_x()) и возвращает мне двойное значение. В истинно функциональной реактивной системе sqrt(x) со временем вернет нового двойника. Если бы вы попытались смоделировать систему FR с помощью #define, вам бы пришлось отказаться от переменных в пользу макросов. Системы FR также обычно пересчитывают данные только тогда, когда их необходимо пересчитать, в то время как использование макросов будет означать, что вы будете постоянно переоценивать все, вплоть до подвыражений. - person Laurence Gonsalves; 26.05.2012
comment
Для многих типов программного обеспечения (например, для всего, что связано с взаимодействием с пользователем) на определенном уровне необходимы побочные эффекты. И возможно только на уровне реализации. Реализация чистого, ленивого функционального программирования имеет множество побочных эффектов, и один из успехов парадигмы состоит в том, что многие из этих эффектов не входят в модель программирования. Мои собственные набеги на функциональные пользовательские интерфейсы показывают, что они также могут быть полностью запрограммированы без побочных эффектов. - person Conal; 21.06.2012
comment
@Conal: Разве взаимодействие с пользователем не является побочным эффектом? - person J D; 26.11.2012
comment
реактивное программирование устраняет необходимость изменять переменные, но при этом позволяет вам делать многое из того, что вы могли бы сделать с помощью мутаций переменных. Похоже, со мной происходит прямо противоположное. x меняется со временем, чем это отличается от его мутации? - person Daniel Kaplan; 30.01.2013
comment
Это описание напоминает мне (синтезируемый) verilog. Подумал, что стоит упомянуть об этом, если это поможет кому-то другому понять концепцию. - person Matthew; 31.01.2013
comment
@tieTYT x никогда не переназначается / не изменяется. Значение x - это последовательность значений во времени. Другой способ взглянуть на это состоит в том, что вместо x, имеющего нормальное значение, например число, значение x является (концептуально) функцией, которая принимает время в качестве параметра. (Это немного упрощение. Вы не можете создать значения времени, которые позволили бы вам предсказать будущее таких вещей, как положение мыши.) - person Laurence Gonsalves; 01.02.2013
comment
Я все еще могу полностью упустить суть, но есть ли идея обновлять каждую переменную каждый тик? Разве это не привело бы к значительному снижению производительности? Например, обновление x, y, minX, minY, maxX, maxY, + других переменных каждый тик может стать дорогостоящим. Однако я подозреваю, что совершенно неправильно понял ваш ответ, и проведу дополнительное исследование. - person spectralbat; 13.03.2013
comment
Разве «функциональный» и «реактивный» не отменяют друг друга в значительной степени? Я не понимаю, насколько функциональна FRP с самыми волшебными и непредсказуемыми переменными. Никаких побочных эффектов !? Вы не представляете, какое значение будет иметь переменная и почему? - person Rudie; 18.04.2013
comment
@Rudie, если мы опишем эти волшебные целые числа по времени как функции времени, некоторые из этих функций будут чистыми, а другие нет. Функция положения указателя мыши определенно нечистая, на самом деле она даже не использует временную переменную. Поэтому я довольно скептически отношусь к этой идее, потому что вы не можете создать чистый графический интерфейс, подобный этому. - person desudesudesu; 06.05.2013
comment
Есть ли на самом деле язык, подобный приведенному в этом ответе, который можно использовать? - person TheIronKnuckle; 14.05.2013
comment
@TheIronKnuckle: есть также Bacon для javascript. - person kumarharsh; 14.12.2013
comment
У меня сейчас открыто около двух десятков вкладок FRP, и это, безусловно, самое лаконичное объяснение, которое я нашел. Вам следует подумать о более подробном описании этого вопроса, например книга. Это горячая тема, и, как я заметил в своем комментарии к родительскому посту, все существующие материалы написаны без учета наивного взгляда на эту тему. - person TechZen; 04.05.2014
comment
Интересно, сколько людей нашли это более полезным только ПОСЛЕ того, как прочитали принятый ответ и не поняли, что принятый ответ подготовил их к пониманию этого. - person David James; 17.03.2015
comment
@Rudie Я думаю, что причина того, что FRP все еще чист, заключается в том, что при одинаковых входных данных вся система всегда будет точно такой же, независимо от того, что было до нее. Итак, в примере с прямоугольником x и y являются функциями над вводом мыши. Если вы вернете мышь туда, где она была раньше, у вас та же система, вы не перейдете к предыдущей системе, просто вы пересчитаете те же входные данные и получите тот же результат. Кроме того, я думаю, что при выполнении FRP вы можете запомнить историю, поэтому все переменные похожи на след. - person Didier A.; 11.04.2015
comment
@DidierA. Сохранение состояния - это механизм краткого описания сложной функции в больших пространствах ввода и вывода путем разложения функции на несколько более простых функций, которые зависят от значения состояния. FRP возвращается к исходной ситуации, когда функции сложны, а пространства ввода и вывода огромны. Это всегда было возможно, поскольку все пространство состояний можно было свернуть во входное пространство. Но обычно это глупо. - person Nimrod; 11.05.2017

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

Это обязательно многое упускает. Метафора довольно быстро разрушается, когда вы действительно используете систему FRP. Во-первых, обычно также предпринимаются попытки смоделировать дискретные события (например, щелчок мышью). Я помещаю это здесь только для того, чтобы вы поняли, на что это похоже.

person Community    schedule 23.06.2009
comment
Крайне уместный пример. Хорошо иметь теоретический материал, и, возможно, некоторые люди понимают это, не прибегая к основному примеру, но мне нужно начать с того, что он делает для меня, а не с того, что он представляет собой абстрактно. То, что я только недавно получил (из разговоров о приемах-приемниках от Netflix!), - это то, что RP (или Rx, во всяком случае) делает эти изменяющиеся значения первоклассными и позволяет вам рассуждать о них или писать функции, которые что-то делают с ними. Если хотите, напишите функции для создания таблиц или ячеек. И он обрабатывает, когда значение заканчивается (исчезает), и позволяет вам очистить автоматически. - person Benjohn; 14.03.2015
comment
В этом примере подчеркивается разница между программированием, управляемым событиями, и реактивным подходом, когда вы просто объявляете зависимости для использования интеллектуальной маршрутизации. - person kinjelom; 24.05.2017

Для меня это примерно два разных значения символа =:

  1. В математике x = sin(t) означает, что x - это другое имя для sin(t). Так что писать x + y - это то же самое, что и sin(t) + y. Функциональное реактивное программирование похоже на математику в этом отношении: если вы напишете x + y, оно будет вычислено с любым значением t в момент его использования.
  2. В языках программирования C-подобных (императивные языки) x = sin(t) - это присвоение: это означает, что x хранит значение sin(t), полученное во время присвоения.
person user712092    schedule 25.05.2012
comment
Хорошее объяснение. Я думаю, вы также можете добавить, что время в смысле FRP - это обычно любое изменение от внешнего входа. Каждый раз, когда внешняя сила изменяет входные данные FRP, вы перемещаете время вперед и снова пересчитываете все, на что влияет изменение. - person Didier A.; 11.04.2015
comment
В математике x = sin(t) означает, что x - это значение sin(t) для данного t. Это не другое название для sin(t) как функции. Иначе было бы x(t) = sin(t). - person Dmitri Zaitsev; 19.05.2016
comment
+ Дмитрий Зайцев Знак равенства в математике имеет несколько значений. Одна из них заключается в том, что всякий раз, когда Вы видите левую сторону, Вы можете поменять местами ее на правую сторону. Например 2 + 3 = 5 или a**2 + b**2 = c**2. - person user712092; 10.11.2016

Хорошо, исходя из базовых знаний и чтения страницы Википедии, на которую вы указали, кажется, что реактивное программирование - это что-то вроде вычислений потока данных, но с определенными внешними «стимулами», запускающими набор узлов для запуска и выполнения своих вычислений.

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

Различные узлы, имеющие края от этого узла «значения объема», будут автоматически запускаться, и любые необходимые вычисления и обновления, естественно, будут проходить через приложение. Приложение «реагирует» на раздражитель пользователя. Функциональное реактивное программирование было бы просто реализацией этой идеи на функциональном языке или вообще в рамках парадигмы функционального программирования.

Чтобы узнать больше о «вычислениях потока данных», поищите эти два слова в Википедии или в своей любимой поисковой системе. Общая идея такова: программа представляет собой ориентированный граф узлов, каждый из которых выполняет простое вычисление. Эти узлы связаны друг с другом связями графа, которые предоставляют выходы одних узлов входам других.

Когда узел запускается или выполняет свои вычисления, соответствующие входы узлов, подключенных к его выходам, «срабатывают» или «отмечаются». Любой узел, у которого все входы активированы / отмечены / доступны, автоматически срабатывает. График может быть неявным или явным, в зависимости от того, как именно реализовано реактивное программирование.

Узлы можно рассматривать как запускаемые параллельно, но часто они выполняются последовательно или с ограниченным параллелизмом (например, их может выполнять несколько потоков). Известным примером является Manchester Dataflow Machine, который (IIRC) использовал архитектуру данных с тегами для запланировать выполнение узлов в графе через один или несколько исполнительных блоков. Вычисления потока данных довольно хорошо подходят для ситуаций, в которых запуск вычислений асинхронно, порождающий каскады вычислений, работает лучше, чем попытки управлять выполнением по часам (или часам).

Реактивное программирование импортирует эту идею «каскада выполнения» и, кажется, думает о программе в стиле потока данных, но с условием, что некоторые из узлов подключены к «внешнему миру», и каскады выполнения запускаются, когда эти сенсорные -подобные узлы меняются. Тогда выполнение программы будет похоже на сложную рефлекторную дугу. Программа может быть или не быть в основном сидячей между стимулами или может переходить в практически сидячее состояние между стимулами.

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

person Thomas Kammeyer    schedule 22.06.2009
comment
Хорошо, теперь есть несколько хороших ответов. Следует ли мне удалить свой пост? Если я увижу, что два или три человека говорят, что он ничего не добавляет, я удалю его, если количество полезных результатов не увеличится. Нет смысла оставлять его здесь, если он не добавляет чего-то ценного. - person Thomas Kammeyer; 23.06.2009
comment
вы упомянули поток данных, так что это добавляет некоторой ценности ИМХО. - person Rainer Joswig; 23.06.2009
comment
Кажется, именно этим и должен быть QML;) - person mlvljr; 14.08.2012
comment
Примером может служить среда разработки MAX / MSP, популярная среди художников-мультимедиа, работающих с обработка аудио и видео. - person bfavaretto; 14.04.2013
comment
Для меня этот ответ был самым простым для понимания, особенно из-за использования естественных аналогов, таких как рябь по приложению и сенсорно-подобные узлы. Большой! - person Akseli Palén; 15.02.2015
comment
Пожалуйста, держите это при себе. Это помогает. Когда вы занимаетесь чем-то новым, несколько точек зрения помогают ему в конечном итоге щелкнуть. - person Charlie Flowers; 27.07.2015
comment
Использование графиков для абстрактных понятий - одна из лучших вещей в информатике. ИМХО легко понять концепцию, если представить ее в виде графика. - person Gabriel; 25.02.2017
comment
К сожалению, связь с Manchester Dataflow Machine не работает. - person Pac0; 24.03.2017
comment
Спасибо Pac0. Я нашел альтернативную ссылку на одну из оригинальных статей об этом и добавил эту ссылку. - person Thomas Kammeyer; 24.03.2017

Прочитав множество страниц о FRP, я наконец наткнулся на это Поучительные статьи о FRP, наконец, заставили меня понять, что такое FRP на самом деле.

Ниже цитирую Генриха Апфельмуса (автора реактивного банана).

В чем суть функционального реактивного программирования?

Распространенным ответом было бы, что «FRP - это все об описании системы в терминах изменяющихся во времени функций, а не изменяемого состояния», и это, конечно, не было бы неправильным. Это семантическая точка зрения. Но, на мой взгляд, более глубокий и удовлетворительный ответ дает следующий чисто синтаксический критерий:

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

Например, возьмем пример счетчика: у вас есть две кнопки с надписью «Вверх» и «Вниз», которые можно использовать для увеличения или уменьшения счетчика. В обязательном порядке вы должны сначала указать начальное значение, а затем изменять его при каждом нажатии кнопки; что-то вроде этого:

counter := 0                               -- initial value
on buttonUp   = (counter := counter + 1)   -- change it later
on buttonDown = (counter := counter - 1)

Дело в том, что во время объявления указывается только начальное значение счетчика; динамическое поведение счетчика неявно присутствует в остальной части текста программы. Напротив, функциональное реактивное программирование определяет все динамическое поведение во время объявления, например:

counter :: Behavior Int
counter = accumulate ($) 0
            (fmap (+1) eventUp
             `union` fmap (subtract 1) eventDown)

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

Итак, в моем понимании программа FRP - это набор уравнений: введите здесь описание изображения

j дискретно: 1,2,3,4 ...

f зависит от t, поэтому это включает возможность моделирования внешних стимулов.

все состояние программы заключено в переменные x_i

Библиотека FRP заботится о времени выполнения, другими словами, переводя j в j+1.

Я объясняю эти уравнения более подробно в этом видео.

РЕДАКТИРОВАТЬ:

Примерно через 2 года после первоначального ответа я недавно пришел к выводу, что реализации FRP имеют еще один важный аспект. Они должны (и обычно решают) важную практическую проблему: недействительность кеша.

Уравнения для x_i-s описывают граф зависимостей. Когда некоторые из x_i изменяются во время j, тогда не нужно обновлять все другие x_i' значения в j+1, поэтому не все зависимости нужно пересчитывать, потому что некоторые x_i' могут быть независимыми от x_i.

Кроме того, x_i-ы, которые действительно изменяются, можно постепенно обновлять. Например, давайте рассмотрим операцию карты f=g.map(_+1) в Scala, где f и g равны List из Ints. Здесь f соответствует x_i(t_j), а g - x_j(t_j). Теперь, если я добавлю элемент к g, было бы расточительно выполнять операцию map для всех элементов в g. Некоторые реализации FRP (например, ) стремятся решить эту проблему. Эта проблема также известна как инкрементные вычисления.

Другими словами, поведение (x_i-s) в FRP можно рассматривать как кэшированные вычисления. Задачей механизма FRP является эффективное аннулирование и повторное вычисление этих кеш-памяти (x_i-s), если некоторые из f_i-s действительно изменяются.

person jhegedus    schedule 31.01.2015
comment
Я был рядом с вами, пока вы не перешли к дискретным уравнениям. Основополагающей идеей FRP было непрерывное время, где нет j+1. Вместо этого подумайте о функциях непрерывного времени. Как показали нам Ньютон, Лейбниц и другие, часто очень удобно (и естественно в буквальном смысле) описывать эти функции дифференциально, но постоянно, используя интегралы и системы ОДУ. В противном случае вы описываете алгоритм приближения (и плохой) вместо самой вещи. - person Conal; 19.07.2016
comment
Язык шаблонов HTML и ограничений макета layx, похоже, выражает элементы FRP. - person ; 11.05.2017
comment
@Conal, это заставляет меня задаться вопросом, чем FRP отличается от ODE. Чем они отличаются? - person jhegedus; 09.08.2017
comment
@jhegedus В этой интеграции (возможно, рекурсивной, то есть ODE) обеспечивается один из строительных блоков FRP, а не все. Каждый элемент словаря FRP (включая, помимо прочего, интеграцию) точно объясняется с точки зрения непрерывного времени. Это объяснение помогает? - person Conal; 19.08.2017

Статья Просто эффективная функциональная реактивность Конала Эллиотта (прямой PDF, 233 КБ) - довольно хорошее введение. Соответствующая библиотека тоже работает.

Этот документ теперь заменен другим документом, Функциональное реактивное программирование типа "пуш-пул" (прямой PDF, 286 КБ) .

person scvalex    schedule 22.06.2009

Отказ от ответственности: мой ответ относится к rx.js - библиотеке «реактивного программирования» для Javascript.

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

Основные преимущества использования наблюдаемого:
i) он абстрагирует состояние от вашего кода, например, если вы хотите, чтобы обработчик событий запускался только для каждого n-го события или прекращал срабатывание после первого n 'событий, или запускать запуск только после первых' n 'событий, вы можете просто использовать HoFs (filter, takeUntil, skip соответственно) вместо установки, обновления и проверки счетчиков.
ii) это улучшает локальность кода - если вы иметь 5 разных обработчиков событий, изменяющих состояние компонента, вы можете объединить их наблюдаемые и вместо этого определить один обработчик событий на объединенном наблюдаемом, эффективно объединяя 5 обработчиков событий в 1. Это позволяет очень легко понять, какие события во всем вашем system может влиять на компонент, поскольку все это присутствует в одном обработчике.

  • Observable - это двойник Iterable.

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

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

person tldr    schedule 26.05.2014
comment
Большое спасибо за это простое определение наблюдаемого и его отличие от итераций. Я думаю, что часто бывает очень полезно сравнить сложную концепцию с ее хорошо известной двойственной концепцией, чтобы получить истинное понимание. - person ; 10.07.2016
comment
Итак, идея FRP заключается в том, что вместо обработки каждого отдельного события создайте поток событий (реализованный с помощью наблюдаемого *) и вместо этого примените к нему HoFs. Я могу ошибаться, но я считаю, что это не так. на самом деле FRP, а скорее хорошая абстракция над шаблоном проектирования Observer, которая позволяет выполнять функциональные операции через HoF (что здорово!), но все еще предназначено для использования с императивным кодом. Обсуждение темы - lambda-the-ultimate.org/node/4982 - person nqe; 10.01.2017

Чувак, это чертовски блестящая идея! Почему я не узнал об этом еще в 1998 году? В любом случае, вот моя интерпретация учебника Fran. Предложения приветствуются, я думаю о запуске игрового движка на его основе.

import pygame
from pygame.surface import Surface
from pygame.sprite import Sprite, Group
from pygame.locals import *
from time import time as epoch_delta
from math import sin, pi
from copy import copy

pygame.init()
screen = pygame.display.set_mode((600,400))
pygame.display.set_caption('Functional Reactive System Demo')

class Time:
    def __float__(self):
        return epoch_delta()
time = Time()

class Function:
    def __init__(self, var, func, phase = 0., scale = 1., offset = 0.):
        self.var = var
        self.func = func
        self.phase = phase
        self.scale = scale
        self.offset = offset
    def copy(self):
        return copy(self)
    def __float__(self):
        return self.func(float(self.var) + float(self.phase)) * float(self.scale) + float(self.offset)
    def __int__(self):
        return int(float(self))
    def __add__(self, n):
        result = self.copy()
        result.offset += n
        return result
    def __mul__(self, n):
        result = self.copy()
        result.scale += n
        return result
    def __inv__(self):
        result = self.copy()
        result.scale *= -1.
        return result
    def __abs__(self):
        return Function(self, abs)

def FuncTime(func, phase = 0., scale = 1., offset = 0.):
    global time
    return Function(time, func, phase, scale, offset)

def SinTime(phase = 0., scale = 1., offset = 0.):
    return FuncTime(sin, phase, scale, offset)
sin_time = SinTime()

def CosTime(phase = 0., scale = 1., offset = 0.):
    phase += pi / 2.
    return SinTime(phase, scale, offset)
cos_time = CosTime()

class Circle:
    def __init__(self, x, y, radius):
        self.x = x
        self.y = y
        self.radius = radius
    @property
    def size(self):
        return [self.radius * 2] * 2
circle = Circle(
        x = cos_time * 200 + 250,
        y = abs(sin_time) * 200 + 50,
        radius = 50)

class CircleView(Sprite):
    def __init__(self, model, color = (255, 0, 0)):
        Sprite.__init__(self)
        self.color = color
        self.model = model
        self.image = Surface([model.radius * 2] * 2).convert_alpha()
        self.rect = self.image.get_rect()
        pygame.draw.ellipse(self.image, self.color, self.rect)
    def update(self):
        self.rect[:] = int(self.model.x), int(self.model.y), self.model.radius * 2, self.model.radius * 2
circle_view = CircleView(circle)

sprites = Group(circle_view)
running = True
while running:
    for event in pygame.event.get():
        if event.type == QUIT:
            running = False
        if event.type == KEYDOWN and event.key == K_ESCAPE:
            running = False
    screen.fill((0, 0, 0))
    sprites.update()
    sprites.draw(screen)
    pygame.display.flip()
pygame.quit()

Вкратце: если каждый компонент можно рассматривать как число, всю систему можно рассматривать как математическое уравнение, не так ли?

person Dan Ross    schedule 13.03.2011
comment
Это немного поздно, но в любом случае ... Frag - это игра, использующая FRP. - person arx; 30.06.2013

Книга Пола Худака The Haskell School of Expression - это не только штраф введение в Haskell, но он также уделяет много времени FRP. Если вы новичок в FRP, я настоятельно рекомендую его, чтобы вы получили представление о том, как работает FRP.

Есть также то, что выглядит как новая версия этой книги (выпущена в 2011 г., обновлена ​​в 2014 г.), Музыкальная школа Haskell.

person cjs    schedule 24.06.2009

Согласно предыдущим ответам, кажется, что математически мы просто мыслим в более высоком порядке. Вместо того чтобы думать, что значение x имеет тип X, мы думаем о функции x: T X, где T - тип времени, будь то натуральные числа, целые числа или континуум. Теперь, когда мы пишем y: = x + 1 на языке программирования, мы фактически имеем в виду уравнение y (t ) = x (t) + 1.

person Yuning    schedule 17.07.2015

Как указано, действует как электронная таблица. Обычно на основе событийно-ориентированной структуры.

Как и в случае со всеми «парадигмами», его новизна спорна.

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

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

Точно так же некоторые состояния могут не быть достигнуты, несмотря на наличие четко определенных границ, потому что глобальное состояние отклоняется от решения. 2 + 2 может или не может стать 4, в зависимости от того, когда 2 стали 2, и остались ли они такими. В электронных таблицах есть синхронные часы и обнаружение петель. Распределенные актеры обычно этого не делают.

Всем хорошего веселья :).

person emperorz    schedule 04.09.2013

Я нашел это красивое видео о FRP в сабреддите Clojure. Это довольно легко понять, даже если вы не знаете Clojure.

Вот видео: http://www.youtube.com/watch?v=nket0K1RXU4

Вот источник, на который ссылается видео во второй половине: https://github.com/Cicayda/yolk-examples/blob/master/src/yolk_examples/client/autocomplete.cljs

person Daniel Kaplan    schedule 17.05.2013

Эта статья Андре Стальца - лучшее и ясное объяснение, которое я когда-либо видел.

Некоторые цитаты из статьи:

Реактивное программирование - это программирование с использованием асинхронных потоков данных.

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

Вот пример фантастических диаграмм, которые являются частью статьи:

Диаграмма потока событий клика

person GreenGiant    schedule 05.11.2016

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

В коде это означает функциональную чистоту и декларативное программирование.

Ошибки состояния - огромная проблема в стандартной императивной парадигме. Различные биты кода могут изменять некоторое совместно используемое состояние в разное «время» выполнения программы. С этим трудно справиться.

В FRP вы описываете (как в декларативном программировании), как данные преобразуются из одного состояния в другое и что его запускает. Это позволяет вам игнорировать время, потому что ваша функция просто реагирует на свои входные данные и использует их текущие значения для создания нового. Это означает, что состояние содержится в графе (или дереве) узлов преобразования и является функционально чистым.

Это значительно снижает сложность и время отладки.

Подумайте о разнице между A = B + C в математике и A = B + C в программе. В математике вы описываете отношения, которые никогда не изменятся. В программе сказано, что «Прямо сейчас» A - это B + C. Но следующей командой может быть B ++, и в этом случае A не равно B + C. В математическом или декларативном программировании A всегда будет равно B + C, независимо от того, в какой момент времени вы спросите.

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

EventStream - это EventStream + некоторая функция преобразования.

Поведение - это поток событий + какое-то значение в памяти.

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

Поведения могут быть составлены для создания нового поведения, которое является преобразованием N других вариантов поведения. Это составленное значение будет пересчитываться при срабатывании входных событий (поведения).

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

Цитата из - Прекращение использования шаблона Observer http://infoscience.epfl.ch/record/148043/files/DeprecatingObserversTR2010.pdf

person Jay Shepherd    schedule 17.06.2015
comment
Именно так я отношусь к декларативному программированию, и вы просто лучше меня описываете эту идею. - person neevek; 09.02.2018

Краткое и ясное объяснение реактивного программирования появляется на странице Cyclejs - Reactive Programming, в нем используется простое и наглядное образцы.

[Модуль / компонент / объект] является реактивным означает, что он полностью отвечает за управление своим состоянием, реагируя на внешние события.

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

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

person pdorgambide    schedule 05.03.2017

Ознакомьтесь с Rx, Reactive Extensions для .NET. Они указывают на то, что с IEnumerable вы в основном «вытаскиваете» из потока. Запросы Linq через IQueryable / IEnumerable - это операции набора, которые «высасывают» результаты из набора. Но с теми же операторами над IObservable вы можете писать запросы Linq, которые «реагируют».

Например, вы можете написать запрос Linq типа (из m в MyObservableSetOfMouseMovements, где m.X ‹100 и m.Y‹ 100 выберите новую точку (m.X, m.Y)).

И с расширениями Rx вот и все: у вас есть код пользовательского интерфейса, который реагирует на входящий поток движений мыши и рисует, когда вы находитесь в поле 100,100 ...

person Sentinel    schedule 25.10.2016

FRP - это комбинация функционального программирования (парадигма программирования, основанная на идее, что все является функцией) и парадигмы реактивного программирования (построенной на идее, что все является потоком (наблюдатель и наблюдаемая философия)). Предполагается, что это будет лучший из миров.

Для начала ознакомьтесь с постом Андре Стальца о реактивном программировании.

person Krishna Ganeriwal    schedule 08.12.2017