Динамическое обновление пользовательского интерфейса на основе предыдущих обновлений

Я занимаюсь исследованием практических аспектов FRP для пользовательского интерфейса, и я изо всех сил пытался реализовать следующие функции с использованием реактивного банана: на основе значения поля выбора отображается переменное количество списков, которые отображают некоторые результаты. (Я использую WxHaskell.)

Было довольно просто реализовать это, используя набор готовых списков, которые скрываются и отображаются в зависимости от поведения результата, но на этот раз я хочу, чтобы он создавал и уничтожал списки по мере необходимости, каждый список был связан с поведением результатов.

Пока у меня есть следующие ингредиенты:

  • событие eParam, привязанное к окну выбора
  • поведение bResults :: Behavior t [[String]], определенное с помощью eParamstepper), которое содержит все результаты (списки элементов в списке)
  • функция обновления updateResultControls :: [SingleListBox ()] -> [[String]] -> IO [SingleListBox ()], которая уничтожает или создает списки на основе результатов. Обратите внимание, что возвращаемый тип находится в IO.

Глядя на пример BarTab , я попытался реализовать следующее:

  • поведение bResultControls :: Behavior t [SingleListBox ()] со списками, определенное как stepper [] eUpdateResultControls.
  • событие eUpdateResultControls :: Event t [SingleListBox ()], которое выполняет обновление пользовательского интерфейса. Это событие зависит от вариантов поведения bResultControls и bResults. Однако он также должен обновить сеть и запустить ввод-вывод, поэтому я подозреваю, что Moment и execute будут задействованы. Здесь я застрял.

Моя последняя попытка такова:

rec
  let
    bResultControls = stepper [] eResultControls
    bResultControlsUpdate = updateResultControls <$> bResultControls <*> bResults

  eResultControls <- execute $ FrameworksMoment . liftIO <$> (bResultControlsUpdate <@ eParam)

Но я получаю следующую ошибку типа:

Couldn't match type `m0 [SingleListBox ()]'
              with `forall t1. Frameworks t1 => Moment t1 [SingleListBox ()]'
Expected type: IO [SingleListBox ()]
               -> forall t. Frameworks t => Moment t [SingleListBox ()]
  Actual type: IO [SingleListBox ()] -> m0 [SingleListBox ()]
In the second argument of `(.)', namely `liftIO'
In the first argument of `(<$>)', namely
  `FrameworksMoment . liftIO'
In the second argument of `($)', namely
  `FrameworksMoment . liftIO <$> (bResultControlsUpdate <@ eParam)'

Я подозреваю, что это потребует обрезки некоторых моделей поведения, или, возможно, я делаю это совершенно неправильно.


person Jacob    schedule 03.08.2014    source источник
comment
(Способ обойти эту проблему, который мне посоветовали использовать в других языках, состоит в том, чтобы иметь фиксированный набор полей и полосу прокрутки. В зависимости от того, что пользователь делает с полосой прокрутки, вы редактируете то, что находится в полях, фактически не перемещая их. 'не создавать произвольное количество ящиков. Мне это никогда не нравилось, поэтому, если есть лучший способ, который не является утечкой памяти, я бы хотел его услышать; спасибо за вопрос.)   -  person AndrewC    schedule 03.08.2014
comment
Вы должны быть осторожны с параметром типа t, который вы можете рассматривать как время начала. Аргумент конструктора FrameworksMoment должен быть полиморфным в t, но в вашем примере это не так, поэтому вы получаете ошибку типа. Я бы порекомендовал более внимательно изучить пример BarTab и понять, почему там присутствует тип AnyMoment, надеюсь, это поможет. Основная проблема заключается в том, что поведение списков начинается в разное время, и для отслеживания этого используется система типов.   -  person Heinrich Apfelmus    schedule 05.08.2014
comment
Привет, прежде всего спасибо за очень интересную работу над реактивным бананом. :) Я думаю, что разница между BarTab и тем, что я пытаюсь сделать, заключается в том, что я хочу использовать существующее поведение для определения этих списков, которые, кажется, пропускают t, связанный networkDescription (то есть исходное время начала) в Moment I Я пытаюсь определить для execute (который в результате не может быть полиморфным. (Кстати, я пробовал с подписями типов.)) Может ли это быть так? (Мне не очень интересно создавать поведение на основе этих новых списков в этом примере, что и делает BarTab.)   -  person Jacob    schedule 06.08.2014


Ответы (1)


Еще немного прочитав и поэкспериментировав, я заставил его работать с некоторой тщательной обрезкой и рефакторингом (как намекнул Генрих):

networkDescription :: forall t. Frameworks t => Moment t ()
networkDescription = do
  eParam <- choiceSelection cParam

  let bResults = results <$> stepper x eParam

  bResults_ <- trimB bResults

  rec
    let
      bResultControls = stepper [] eResultControls

      mkResultControls :: [SingleListBox ()] -> [[String]] -> FrameworksMoment [SingleListBox ()]
      mkResultControls cs rs = FrameworksMoment $ do
        slResults <- liftIO $ updateResultControls cs rs

        bResults <- now bResults_

        sequence_ [sink sl [items :== (!! i) <$> bResults] | sl <- slResults | i <- [0..]]

        liftIO $ do
          let n = length rs
          set f [clientSize := sz (150 * n) 200]
          set pResults [layout := fill $ boxed "results" $ row n (map (fill . widget) slResults)]
          refit f

        return slResults

    eResultControls <- execute $ (mkResultControls <$> stepper [] eResultControls <*> bResults) <@ eParam

  return ()

(Теперь есть небольшая ошибка, когда событие срабатывает до обновления поведения, но это должно быть легко исправить.)

person Jacob    schedule 06.08.2014
comment
Выглядит хорошо для меня. Примечание. Использование stepper [] eResultControls в предпоследней строке можно заменить на bResultControls. Что касается запуска события перед поведением: это всегда так. Возможно, вам придется использовать accumE, чтобы получить желаемую одновременность, но я не уверен, что это хорошо работает с execute. В общем, использование функций в IO для вычисления значений событий несколько обескураживает, потому что порядок действий IO не определен. Однако иногда вам нужно это сделать, поэтому у нас есть execute и reactimate. - person Heinrich Apfelmus; 07.08.2014