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