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

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

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

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

18 ответов

Решение

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

rectangle(minX, minY, maxX, maxY)

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

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

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

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

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

  1. По математике x = sin(t) Значит это x это другое название для sin(t), Итак, написание x + y это то же самое, что sin(t) + y, В этом отношении функционально-реактивное программирование похоже на математику: если вы пишете x + yвычисляется с любым значением t в то время, когда он используется.
  2. В C-подобных языках программирования (императивных языках) x = sin(t) это назначение: это означает, что x хранит значение sin(t) взятый во время назначения.

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

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

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

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

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

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

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

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

Прочитав много страниц о 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-S, которые делают изменения могут быть постепенно обновлены. Например, давайте рассмотрим операцию с картой f=g.map(_+1) в Скале, где f а также g являются List из Ints, Вот f соответствует x_i(t_j) а также g является x_j(t_j), Теперь, если я добавлю элемент к g тогда было бы расточительно выполнять map операция для всех элементов в g, Некоторые реализации FRP (например, reflex-frp) направлены на решение этой проблемы. Эта проблема также известна как инкрементные вычисления.

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

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

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

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

  • Observable - это двойственное от Iterable.

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

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

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

Теперь этот документ заменен другим документом, функциональным реактивным программированием Push-pull ( прямой PDF, 286 КБ).

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

Проверьте Rx, Реактивные расширения для.NET. Они указывают, что с IEnumerable вы в основном "вытягиваете" из потока. Linq-запросы к IQueryable/IEnumerable являются операциями над множествами, которые "высасывают" результаты из набора. Но с теми же операторами над IObservable вы можете писать запросы Linq, которые "реагируют".

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

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

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

Посмотрите на статью Андреа Штальца о реактивном программировании.

Другие вопросы по тегам