Я не уверен, ожидается ли такое поведение (т.е. я неправильно использую Reactive.Banana.Switch) или это ошибка.
Допустим, у меня есть два входных поведения с одинаковым типом, и я хочу переключаться между ними на основе события. Я написал эту функцию:
switchBehaviors ::
Behavior t a -- | Behavior to yield initially and after "True" events
-> Behavior t a -- | Behavior to yield after "False" events
-> Event t Bool -- | Select between behaviors
-> Moment t (Behavior t a)
switchBehaviors t f es = do
t' <- trimB t
f' <- trimB f
return $ switchB t $ (\e -> if e then t' else f') <$> es
Этот код кажется достаточно безобидным; он проверяет тип, компилирует и дает желаемый результат при внедрении в простой макет графического интерфейса. (Два текстовых поля ввода для вариантов поведения, кнопка, выдающая чередующиеся истинные и ложные события, и метка, привязанная к комбинированному поведению с помощью sink
.)
Однако после нескольких срабатываний Event становится очевидным, что где-то происходит катастрофическая утечка. Приложение начинает все дольше и дольше реагировать как на изменения входного поведения, так и на новые события. Он также начинает есть память.
Вот профиль кучи с -hC: Я постоянно переключаю событие; два самых больших всплеска — это, возможно, двадцатая и двадцать первая вспышки События.
Использование trimB немного похоже на махание рукой, чтобы сложить типы; Я не знаю, правильно ли я использую его или как-то злоупотребляю им.
Мои подвопросы:
1) Я злоупотребляю API Reactive.Banana.Switch или это ошибка? Если я злоупотребляю API, что я делаю не так?
2) Должен ли я делать это без использования динамического переключения событий? Использование apply
не дает правильного поведения, потому что результирующее событие не срабатывает при изменении основного поведения. Если я разверну все три входа в Events, я думаю, что смогу настроить складку, вручную накапливая самое последнее значение каждого input Event. Это правильный подход?