имеет не только один, но и три ответа.Краткие ответы: 1. обычно нет, 2. иногда да, 3. с обходным решением да.
Здесь длинные ответы.
Семантика реактивного банана не поддерживает определение Event
непосредственно в терминах самого себя .
Это решение было принято Коналом Эллиоттом в его оригинальной семантике FRP, и я решилпридерживаться этого.Его главное преимущество заключается в том, что семантика остается очень простой, вы всегда можете думать с точки зрения
type Behavior a = Time -> a
type Event a = [(Time,a)]
Я предоставил модуль Reactive.Banana.Model , который реализует почти точно эту модель,Вы можете обратиться к его исходному коду для любых вопросов, касающихся семантики реактивного банана.В частности, вы можете использовать его для рассуждения о своем примере: расчет с ручкой и бумагой или попытка его в GHCi (с некоторыми ложными данными) скажет вам, что значение evtAutoLayout
равно _|_
, то есть не определено.
Последнее может удивлять, но, как вы написали, пример действительно не определен: состояние GUI изменяется только в случае события evtAutoLayout
, но это может произойти, только если вы знаете, изменяется ли состояние GUI,который в свою очередь и т. д. Вы всегда должны разорвать петлю обратной связи удушения с помощью , вставив небольшую задержку .К сожалению, реактивный банан в настоящее время не предлагает способ вставки небольших задержек, главным образом потому, что я не знаю, как описать небольшие задержки в терминах модели [(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
, хотя он может выполняться слишком рано.Возможно, вам придется немного поэкспериментировать, чтобы найти хорошее решение.
Надеюсь, это поможет;Не стесняйтесь спрашивать разъяснения, примеры и т. д.
Раскрытие информации: я являюсь автором библиотеки реактивных бананов.