reactive-banana-wx `sink` не генерирует событие, разрешающее обработку после приемника

В настоящее время я переделываю часть устаревшего wxHaskell, используя пакеты reactive-banana и reactive-banana-wx. Однако, чтобы избежать построения динамической сети (где я столкнулся с блокировкой потока в MVar), теперь я имитирую это, заранее создавая фиксированный набор виджетов wxHaskell, для которых я устанавливаю видимость по мере необходимости. Видимость устанавливается функцией sink, принимающей Behavior. Однако wxHaskell требует, чтобы после того, как все эти виджеты были должным образом изменены с помощью sink, требовалось последующее изменение макета панели, содержащей эти виджеты. Это означало бы, что sinking на самом деле должен быть частью сети, так что это событие, которое можно инициировать и ждать изменения макета. В настоящее время sink выводит вас из сети событий, невозможно инициировать событие после завершения действия sink. Я пытался адаптировать sink к чему-то вроде этого:

sink' :: Frameworks t =>
    w -> [Prop' t w] -> Moment t (Event t ())
sink' widget props = do
    es <- mapM sink1 props
    return $ unions es
  where
    sink1 (attr :== b) = do
        x <- initial b
        liftIOLater $ set widget [attr := x]
        e <- changes b
        return $ (\x -> unsafePerformIO $ set widget [attr := x]) <$> e

Однако unsafePerformIO не был выполнен. Как можно добиться желаемого поведения, то есть разрешить ожидание ввода-вывода (wxHaskell) с помощью Event?


person atze    schedule 27.11.2012    source источник


Ответы (1)


По сути, похоже, вы хотите убедиться, что действия ввода-вывода в reactimate выполняются в определенном порядке? А именно, вы хотите убедиться, что макет установлен после установки свойств виджета.

Существует несколько способов указать порядок:

  1. Используйте union, unionWith и/или collect, чтобы определить порядок одновременных событий.
  2. Используйте тот факт, что reactimate выполняются в том порядке, в котором они появляются в монаде Moment. (Хотя, строго говоря, это уже не так, когда вы используете комбинатор observeE из динамического переключения событий.)

В вашем конкретном случае эти идеи можно применить следующим образом.

Для 1 вы можете сделать событие, содержащее действие ввода-вывода, и позже объединить его с макетом.

sink' :: Frameworks t =>
    w -> [Prop' t w] -> Moment t (Event t (IO ()))
sink' widget props = do
    es <- mapM sink1 props
    return $ foldr1 (unionWith (>>)) es
  where
    sink1 (attr :== b) = do
        x <- initial b
        liftIOLater $ set widget [attr := x]
        e <- changes b
        return $ (\x -> set widget [attr := x]) <$> e

Для 2 вы можете просто использовать обычные функции sink и просто убедиться, что макет установлен последним.

do
    sink widget1 [ visible :== bBool ]
    sink window1 [ layout  :== bLayout ]

Порядок sink функций в монаде гарантирует, что макет будет установлен последним.


Также обратите внимание, что, начиная с версии reactive-banana 0.7, вы можете использовать динамическое переключение событий для моделирования переменного набора виджетов. См. пример BarTab.hs для демонстрации. В этом примере также задается макет.

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

person Heinrich Apfelmus    schedule 27.11.2012
comment
Спасибо! Что касается примера BarTab.hs, я скопировал его поведение. BarTab динамически вычисляет приращение списка виджетов, это приращение не зависит от предыдущего списка. Однако приращение, которое я пытался создать, зависит от предыдущего списка. Я предполагаю, что там что-то пошло не так, и, вероятно, повторю попытку позже, что может привести к другому посту. - person atze; 29.11.2012
comment
Хорошо, дайте мне знать, если что-то еще появится. - person Heinrich Apfelmus; 29.11.2012