Может ли реактивный банан обрабатывать циклы в сети?
У нас есть такой код:
guiState :: Discrete GuiState
guiState = stepperD (GuiState []) $
union (mkGuiState <$> changes model) evtAutoLayout
evtAutoLayout :: Event GuiState
evtAutoLayout = fmap fromJust . filterE isJust . fmap autoLayout $ changes guiState
Вы можете видеть, что evtAutoLayout подается в guiState, а затем в evtAutoLayout - так что там есть цикл. Это намеренно. Автоматическая разметка регулирует состояние графического интерфейса до тех пор, пока оно не достигнет равновесия, а затем ничего не вернет, поэтому он должен остановить цикл. Разумеется, новая смена модели может запустить ее снова.
Когда мы соединяем это вместе, мы сталкиваемся с бесконечным циклом при вызове функции компиляции. Даже если autoLayout = Nothing, это все равно приводит к переполнению стека во время компиляции.
Если я удаляю вызов объединения в guiState и удаляю evtAutoLayout из картинки...
guiState :: Discrete GuiState
guiState = stepperD (GuiState []) $ mkGuiState <$> changes model
это работает отлично.
Какие-либо предложения?
1 ответ
Вопрос
Поддерживает ли библиотека реактивного банана рекурсивно определенные события?
имеет не только один, но и три ответа. Краткие ответы: 1. как правило, нет, 2. иногда да, 3. с обходным путем да.
Здесь длинные ответы.
Семантика реактивного банана не поддерживает определение
Event
прямо с точки зрения самого себя.Это решение, которое Конал Эллиотт принял в своей оригинальной семантике FRP, и я решил придерживаться его. Его главное преимущество в том, что семантика остается очень простой, вы всегда можете думать с точки зрения
type Behavior a = Time -> a type Event a = [(Time,a)]
Я предоставил модуль Reactive.Banana.Model, который реализует почти точно эту модель, вы можете обратиться к его исходному коду по любым вопросам, касающимся семантики реактивного банана. В частности, вы можете использовать его для рассуждения о своем примере: расчет с ручкой и бумагой или попытка его в GHCi (с некоторыми ложными данными) скажет вам, что значение
evtAutoLayout
равно_|_
т.е. не определено.Последнее может быть удивительным, но, как вы написали, пример действительно не определен: состояние графического интерфейса меняется только в том случае, если
evtAutoLayout
Событие происходит, но это может произойти, только если вы знаете, изменяется ли состояние графического интерфейса, что, в свою очередь, и т. д. Вам всегда нужно разорвать петлю удушающей обратной связи, вставив небольшую задержку. К сожалению, реактивный банан в настоящее время не предлагает способ вставить небольшие задержки, в основном потому, что я не знаю, как описать небольшие задержки с точки зрения[(Time,a)]
модель таким образом, что позволяет рекурсию. (Но см. Ответ 3.)Можно и рекомендуется определить
Event
с точки зренияBehavior
это снова относится к событию. Другими словами, рекурсия разрешена, пока вы проходите Поведение.Простой пример будет
import Reactive.Banana.Model filterRising :: (FRP f, Ord a) => Event f a -> Event f a filterRising eInput = eOutput where eOutput = filterApply (greater <$> behavior) eInput behavior = stepper Nothing (Just <$> eOutput) greater Nothing _ = True greater (Just x) y = x < y example :: [(Time,Int)] example = interpretTime filterRising $ zip [1..] [2,1,5,4,8,9,7] -- example = [(1.0, 2),(3.0, 5),(5.0, 8),(6.0, 9)]
Учитывая поток событий, функция
filterRising
возвращает только те события, которые больше, чем ранее возвращенные. На это намекает документация дляstepper
функцияОднако, это, вероятно, не тот тип рекурсии, который вы желаете.
Тем не менее, можно добавить небольшие задержки в реактивном банане, он просто не является частью базовой библиотеки и, следовательно, не имеет гарантированной семантики. Кроме того, вам нужна некоторая поддержка вашего цикла событий, чтобы сделать это.
Например, вы можете использовать wxTimer, чтобы запланировать событие, которое произойдет сразу после обработки текущего события. Пример Wave.hs демонстрирует рекурсивное использование wxTimer с реактивным-бананом. Я не совсем знаю, что происходит, когда вы установите интервал таймера
0
Впрочем, он может исполниться слишком рано. Возможно, вам придется немного поэкспериментировать, чтобы найти хорошее решение.
Надеюсь, это поможет; не стесняйтесь спрашивать разъяснения, примеры и т. д.
Раскрытие: я являюсь автором реактивно-банановой библиотеки.