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

17

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

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

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

Например, если вы пишете счетчик, вам не нужны временные метки времени и времени, вам просто нужно, чтобы события «Увеличено нажатие кнопки» и «Уменьшение нажатой кнопки».
Если вы пишете игру и хотите поведение позиции / силы, вы просто заботитесь о событиях, связанных с 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

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

  

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

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

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

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

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

Документ «Конус-Эллиотт», написанный Коналом Эллиоттом описывает события, изменяющиеся во времени, где единственные моменты времени, Интересны случаи, когда происходят события. Reactive переменные события - текущее значение и следующий Event , которые изменят его. % Co_de% является Event в изменяющихся по событию Future данных.

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

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

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

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

type Behavior a = Reactive (Time -> a)

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

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