Является ли «сигнальное» представление функционально-реактивного программирования правильным? - PullRequest
16 голосов
/ 17 сентября 2011

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

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

time.each { t => print(t) } // every time there is a 'tick' the current time is printed
a = time * 5 //where 'a' would be a dynamic value always up to date with time

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

1 Ответ

14 голосов
/ 17 сентября 2011

К сожалению, объединение «события» и «поведения» в один «сигнал» объекта не работает так хорошо.

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

type Event a = Signal (Maybe a)

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

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

type Behavior a = Time -> a
-- = function that takes the current time as parameter and returns
--   the corresponding value of type  a

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

type Event a = [(Time,a)]
-- list of pairs of the form (current time, corresponding event value)

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

Аргумент в пользу различия между поведением и событиями заключается в том, что их API совершенно разные. Суть в том, что у них разные виды продукции:

(Behavior a , Behavior b) = Behavior (a,b)
(Event a    , Event b   ) = Event (a :+: b)

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

(<*>) :: Behavior (a -> b) -> Behavior a -> Behavior b
apply :: Behavior (a -> b) -> Event a    -> Event b

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

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

(Полное раскрытие: я реализовал библиотеку FRP под названием реактивный-банан в Haskell.)

...