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

Я не уверен, ожидается ли такое поведение (т.е. я неправильно использую 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. Это правильный подход?


person Christian Conkle    schedule 13.03.2013    source источник
comment
Версия 0.7 reactive-banana еще не реализует сборку мусора для динамически переключаемых событий, поэтому все очень динамичное, скорее всего, займет место. Я работаю над этим. :-)   -  person Heinrich Apfelmus    schedule 14.03.2013
comment
@HeinrichApfelmus Эта ошибка все еще присутствует? Сегодня я провел эксперимент с динамическим переключением (по нажатию клавиши), и после 10-20 нажатий моя программа переставала отвечать на запросы и как минимум один раз даже вылетала с переполнением стека.   -  person Jason Dagit    schedule 20.06.2013
comment
@JasonDagit Версия для разработки исправляет несколько дополнительных утечек пространства, которые могут способствовать возникновению проблемы. Тем не менее, я до сих пор не дошел до реализации сборки мусора для динамического переключения событий.   -  person Heinrich Apfelmus    schedule 20.06.2013


Ответы (1)


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

switchBehaviors 
    :: Behavior t a    -- | Behavior to yield when it's "True"
    -> Behavior t a    -- | Behavior to yield when it's "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 все еще очень экспериментальный, и его характеристики улучшаются.)

person Christian Conkle    schedule 16.03.2013