Почему FRP рассматривает время как фактор для значений?

17

Поведение повсеместно определяется как «изменяющееся во времени значение» s 1 .

Почему? время зависимости / параметра для изменяющихся значений очень редко.

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

Например, если вы пишете счетчик, вы не заботитесь о времени / связанных временных метках, вы просто заботитесь о событиях «Нажатие кнопки увеличения» и «Нажатие кнопки уменьшения».
Если вы пишете игру и хотите использовать позицию / силу, вы просто заботитесь о проводимых событиях с помощью клавиш WASD / стрелок и т. Д. (Если только вы не запретите игрокам переходить влево во второй половине дня; как это несправедливо!).

Итак: почему время вообще нужно учитывать? почему метки времени? почему некоторые библиотеки (например, reactive-banana , reactive ) принимают его до такой степени, чтобы иметь значения Future , Moment ? Зачем работать с потоками событий, а не просто отвечать на события? Все это просто усложняет простую идею (значение, зависящее от события / события); в чем выгода? какую проблему мы здесь решаем? (Я хотел бы также получить конкретный пример и замечательное объяснение, если это возможно).

1 Определено поведение, поэтому здесь , здесь , здесь ... & amp; почти везде, где я встречался.

    
задан MasterMastic 29.08.2014 в 14:53
источник
  • Время непрерывное, тики нет. «Время» даже не обязательно относится к реальному времени, это может быть симуляция или все, что втекает в одно направление. –  Bergi 29.08.2014 в 16:04
  • @Bergi Извините меня, если это глупый вопрос, но почему нас должно волновать, является ли то, что движет нашими ценностями, непрерывным или нет? –  MasterMastic 30.08.2014 в 12:25
  • Нет, это не глупо. Хорошим свойством о непрерывных значениях является то, что они не управляются, но могут быть опрошены - они представляют значение в любое произвольное время. –  Bergi 30.08.2014 в 13:10
  • @Bergi Разве это не означает, что мы не можем реагировать на них (или вообще вообще)? например, как можно реактивировать работу? если бы он просто опросил, пока не увидит изменение в потоке, это будет очень неэффективно, не так ли? –  MasterMastic 30.08.2014 в 13:17
  • Да, afaik вам нужно попробовать Поведение к событиям для обработки их дискретно. Фактически ли они реализованы неэффективно, это другой вопрос, в котором подробно описывается «push-pull FRP» компании ConalElliot. –  Bergi 30.08.2014 в 14:16

3 ответа

12

Behavior s отличается от Event s прежде всего тем, что Behavior имеет значение прямо сейчас , тогда как Event имеет значение только при появлении нового события.

Итак, что мы подразумеваем под «прямо сейчас»? Технически все изменения реализованы в виде семантики push или pull для потоков событий, поэтому мы можем только иметь в виду «самое последнее значение на момент последнего события последствий для этого Behavior ». Но это довольно волосатое понятие - на практике «сейчас» намного проще.

Обоснование того, почему «сейчас» проще, сводится к API. Вот два примера из Reactive Banana.

  1. В конце концов, система FRP всегда должна вызывать какие-то внешне видимые изменения. В Reactive Banana этому способствует функция reactimate :: Event (IO ()) -> Moment () , которая потребляет потоки событий . Нет способа заставить Behavior инициировать внешние изменения - вам всегда нужно делать что-то вроде reactimate (someBehavior <@ sampleTickEvent) , чтобы проверить поведение в конкретные моменты времени.

  2. Поведение Applicative s в отличие от Event s. Зачем? Хорошо, давайте предположим, что Event был аппликативным и подумаем о том, что происходит, когда у нас есть два потока событий f и x и пишем f <*> x : поскольку события происходят все в разное время, шансы f и x , определяемые одновременно, равны ( почти наверняка ) 0. Так что f <*> x всегда будет означать пустой поток событий, который бесполезен.

    Вы действительно хотите, чтобы f <*> x кэшировал самые последние значения для каждого и принимал их объединенное значение «все время». Это действительно запутанная концепция для обсуждения в терминах потока событий, поэтому вместо этого давайте рассмотрим f и x как принимающие значения для всех моментов времени. Теперь f <*> x также определяется как получение значений для всех моментов времени. Мы только что изобрели Behavior s.

ответ дан J. Abrahamson 29.08.2014 в 15:25
  • Обратите внимание, что f <*> x определяется для событий, если время дискретизировано ... Но теперь вы должны знать о дискретном времени! –  J. Abrahamson 29.08.2014 в 16:29
  • Пункт 2. требует, чтобы значение определялось во всех точках времени, а не во всех точках времени. Нам не нужны Поведения, которые являются функциями времени, пока мы не хотим, чтобы значение изменялось в определенный момент времени, когда событие не происходило. –  Cirdec 30.08.2014 в 15:10
  • Это выбор разделения реализации от интерфейса. Идея FRP заключается в том, что вам обычно интересно говорить о потоках событий всякий раз, когда вы используете эту модель IORef. Тем не менее, использование IORef является лишь проблемой реализации и может быть неэффективным. Если вы укажете свою проблему с использованием примитивов FRP, автор библиотеки попытается оптимизировать, как происходит семантика, пока ваш код остается на высоком уровне и «интуитивным». Честно говоря, если вам просто нужен статический FRP, тогда это очень просто (все монады исчезают). –  J. Abrahamson 30.08.2014 в 17:30
  • При выборе абстракции часто следует учитывать наименее мощную абстракцию. Если все, что вам нужно, это Functor, ваш код будет работать практически со всем, если все, что вам нужно, является аппликативным, это не совсем так, как обычно, но все же многое может обеспечить его. Если вам нужна Монада, все в порядке; есть много вещей, которые могут быть Монадами. Если вам нужна определенная Monad, например IO для IORef, тогда ваш код будет работать только с вещами, созданными поверх IO. «Комплексный (часто монодичный) тип« проще, чем IORef. –  Cirdec 31.08.2014 в 06:21
  • Нестатический FRP - это поведение (поведение a) -> Поведение a. Он не сочетается с накоплением и ведет к серьезным проблемам с производительностью в, казалось бы, безобидном коде. Многие монадические кривые в библиотеках FRP имеют дело с невозможностью выразить эти ненадежные программы, обеспечивая динамические сигналы и накопление. –  J. Abrahamson 31.08.2014 в 19:02
Показать остальные комментарии
12

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

Why? time being the dependency/parameter for varying values is very uncommon.

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

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

Если вы действительно хотите понять суть и мысли о FRP, я рекомендую вам прочитать мой ответ на" Спецификацию языка функционального реактивного программирования " и следуйте указателям, включая " Что такое функционально-реактивное программирование ".

    
ответ дан Conal 07.09.2014 в 00:38
4

В документе Push-Pull FRP Конала Эллиота описываются изменяющиеся события данные, где единственные моменты времени, которые Интересно, когда события происходят. Reactive изменяющихся событий данных - это текущее значение и следующий Event , который изменит его. Event - это точка Future в изменяющихся событиях Reactive данных.

data Reactive a = a ‘Stepper ‘ Event a
newtype Event a = Ev (Future (Reactive a))

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

Reactive a имеет значение для a во все моменты времени, так зачем нам нужен Behavior s? Давайте сделаем простую игру. Между тем, когда пользователь нажимает клавиши WASD, персонаж, ускоренный силой, все еще перемещается на экране. Положение персонажа в разные моменты времени отличается, даже если за прошедшее время не произошло ни одного события. Это то, что описывает Behavior - то, что имеет значение не только во все моменты времени, но и может быть разным во все моменты времени, даже без промежуточных событий.

Один из способов описать Behavior s - повторить то, что мы только что заявили. Behavior s - это вещи, которые могут меняться между событиями. Промежуточные события - это изменяющиеся во времени значения или функции времени.

type Behavior a = Reactive (Time -> a)

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

    
ответ дан Cirdec 30.08.2014 в 14:54