Динамическое переключение событий в реактивном банане вызывает сильную утечку

Я не уверен, ожидается ли это поведение (т.е. я неправильно использую 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: leaking memory Я неоднократно переключаю событие; два крупнейших шипа, возможно, двадцатый и двадцать первый выстрелы в Событие.

Использование trimB немного напоминает размахивание рук, чтобы добавить типы; Я не знаю, правильно ли я его использую или злоупотребляю.

Мои вопросы:

1) Я злоупотребляю Reactive.Banana.Switch API, или это ошибка? Если я злоупотребляю API, что я делаю неправильно?

2) Должен ли я делать это без использования динамического переключения событий? Использование apply не дает правильного поведения, потому что результирующее событие не срабатывает при изменении базового поведения. Если я развожу все три входа в Events, я предполагаю, что смогу создать сводку, вручную накапливая последнее значение каждого входного события. Это правильный подход?

Ответ 1

Такое поведение фактически тривиально реализовать без динамического переключения, если вы используете Behavior для ввода выбора и напомните, что Behavior является экземпляром Applicative. Я не очень усвоил идиому f <$> x <*> y <*> z ..., когда задавал этот вопрос, поэтому здесь явная разработка для других, таких как я:

switchBehaviors 
    :: Behavior t a    -- | Behavior to yield when it "True"
    -> Behavior t a    -- | Behavior to yield when it "False"
    -> Behavior t Bool -- | Select between behaviors
    -> Behavior t a
switchBehaviors t f es = 
    (\e x y -> if e then x else y) <$> es <*> t <*> f

(Генрих Апфельмус обратился к первому вопросу в комментарии. Как он отмечает, Reactive.Banana.Switch все еще очень экспериментальный, и его рабочие характеристики улучшаются.)