Я не уверен, ожидается ли это поведение (т.е. я неправильно использую Reactive.Banana.Switch) или ошибку.
Скажем, у меня есть два типизированных ввода Behaviors, и я хочу переключаться между ними на основе события. Я написал эту функцию:
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
Этот код кажется достаточно невинным; он проверяет, компилирует и дает желаемый результат при встраивании в простой макет графического интерфейса пользователя. (Два поля ввода текста для Behaviors, кнопка, испускающая альтернативные True и False Events, и метка, связанная с комбинированным Поведением с использованием sink
.)
Однако, после запуска события несколько раз, становится очевидным, что там где-то происходит утечка. Приложение начинает все больше и больше реагировать как на изменения в поведении, так и на новые события. Он также начинает есть память.
Здесь куча профиля с -hC: Я неоднократно переключаю событие; два крупнейших шипа, возможно, двадцатый и двадцать первый выстрелы в Событие.
Использование trimB немного напоминает размахивание рук, чтобы добавить типы; Я не знаю, правильно ли я его использую или злоупотребляю.
Мои вопросы:
1) Я злоупотребляю Reactive.Banana.Switch API, или это ошибка? Если я злоупотребляю API, что я делаю неправильно?
2) Должен ли я делать это без использования динамического переключения событий? Использование apply
не дает правильного поведения, потому что результирующее событие не срабатывает при изменении базового поведения. Если я развожу все три входа в Events, я предполагаю, что смогу создать сводку, вручную накапливая последнее значение каждого входного события. Это правильный подход?