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

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

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

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

Ответ 1

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

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

Я действительно резонирую с простым описанием Laurence G, что FRP относится к "типам данных, которые представляют ценность" с течением времени ". Обычное императивное программирование фиксирует эти динамические значения только косвенно, через состояние и мутации. Полная история (прошлое, настоящее, будущее) не имеет представления первого класса. Более того, только дискретно меняющиеся ценности могут (косвенно) захватываться, поскольку императивная парадигма временно дискретна. Напротив, FRP фиксирует эти изменяющиеся значения напрямую и не имеет никаких проблем с постоянно меняющимися значениями.

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

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

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

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

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

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

  • Чтобы вы знали, что находитесь на твердой почве, дайте целой модели композиционную основу, используя технику денотационной семантики, которая просто означает, что (а) каждый тип имеет соответствующий простой и точный математический тип "значения" и (б) каждый примитив, а оператор имеет простой и точный смысл как функцию значений составляющих. Никогда, никогда не смешивайте соображения, связанные с внедрением, в процессе исследования. Если это описание является тарабарщиной для вас, проконсультируйтесь (a) Denotational design с морфизмами типа класса, (b) Push-pull функциональное реактивное программирование (игнорирование битов реализации) и (c) Denotational Semantics Haskell wikibooks page. Остерегайтесь того, что денотационная семантика состоит из двух частей: от двух ее основателей Кристофера Стрэхи и Даны Скотт: более простая и полезная часть Страхи и более трудная и менее полезная (для разработки программного обеспечения) часть Скотта.

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

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

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

Ответ 2

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

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

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

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

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 будет нарисовано вокруг указателя мыши и будет отслеживать его везде, где оно перемещается.

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

Ответ 3

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

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

Ответ 4

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

  • В математике x = sin(t) означает, что x другое имя для sin(t). Таким образом, запись x + y - это то же самое, что и sin(t) + y. Функциональное реактивное программирование похоже на математику в этом отношении: если вы пишете x + y, он вычисляется с помощью любого значения t в момент его использования.
  • В C-подобных языках программирования (императивные языки) x = sin(t) - это назначение: это означает, что x хранит значение sin(t), взятое во время назначения.

Ответ 5

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

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

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

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

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

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

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

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

Ответ 6

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

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

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

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

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

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

  counter: = 0 - начальное значение
on buttonUp = (счетчик: = счетчик + 1) - изменить его позже
on buttonDown = (счетчик: = счетчик - 1)
Код>

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

  counter:: Behavior Int
counter = accumulate ($) 0           (fmap (+1) eventUp            `union` fmap (вычесть 1) eventDown)
Код>

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

Итак, в моем понимании программа FRP представляет собой набор уравнений: введите описание изображения здесь>></p>  <p> <code> j</code> является дискретным: 1,2,3,4...</p>  <p> <code> f</code> зависит от  <code> t</code>, поэтому это включает возможность моделирования внешних стимулов</p>  <p>все состояние программы инкапсулируется в переменные  <code> x_i</code></p>  <p>Библиотека FRP заботится о времени выполнения, другими словами, принимая  <code> j</code> в  <code> j + 1</code>.</p>  <p>Я объясняю эти уравнения гораздо более подробно в <a href=этом видео.

EDIT:

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

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

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

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

Ответ 7

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

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

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

  • Наблюдаемое - это двойственное значение Итерабельного.

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

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

Ответ 9

Чувак, это грандиозная блестящая идея! Почему я не узнал об этом еще в 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()

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

Ответ 10

Книга Пол Худака, Школа выражения Haskell, является не только прекрасным введением в Haskell, но и также проводит много времени на FRP. Если вы новичок в FRP, я настоятельно рекомендую вам дать вам представление о том, как работает FRP.

Существует также то, что выглядит как новый переписчик этой книги (выпущен в 2011 году, обновлен 2014), Школа музыки Haskell.

Ответ 11

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

Ответ 12

Действует как электронная таблица, как указано. Обычно основывается на платформе, управляемой событиями.

Как и во всех "парадигмах", эта новизна спорна.

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

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

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

Все хорошее развлечение:).

Ответ 14

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

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

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

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

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

Нажмите диаграмму потока событий

Ответ 15

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

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

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

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

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

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

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

EventStream - это функция трансформации EventStream +.

A Поведение - это значение EventStream + Some в памяти.

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

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

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

Цитата из - Устаревший шаблон наблюдателя http://infoscience.epfl.ch/record/148043/files/DeprecatingObserversTR2010.pdf

Ответ 16

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

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

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

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

Ответ 17

Реактивное программирование имеет дело с событиями и функциональными средствами, что мы можем отслеживать некоторые функциональные стили (map, reduce и т.д.), работающие с потоками, так что это простые данные.

Но более понятно это понять на практических примерах. Вы можете взглянуть на эту статью: http://yarax.ru/2016/03/06/reactive-programming/

Описаны некоторые практические проблемы, которые могут быть решены с помощью реактивного программирования на клиенте (взаимодействие браузера JavaScript) на стороне сервера (Node.js).

Например, на сервере мы можем использовать FRP-методы, чтобы убедиться, что несколько служб выбрали "подключенное" событие. И после того, как он сделает все, что нам нужно. Это будет выглядеть намного лучше, чем обрабатывать каждое событие, когда некоторые флаги решают одну и ту же проблему.

Примеры даются с использованием JavaScript Kefir.js, но он очень похож на другие библиотеки, такие как Rx.js и Bacon.js

Ответ 18

Проверьте 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...

Ответ 19

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

Отправляйте сообщение Andre Staltz на реактивное программирование для начала.