Переключение на событие, заключенное в монадический контекст

Моя конкретная проблема такова:

Учитывая Event t [a] и Event t () (скажем, это событие тик), я хочу произвести Event t aто есть событие, которое дает мне последовательные элементы из списка ввода для каждого события тика.

Рефлекс имеет следующий помощник:

zipListWithEvent :: (Reflex t, MonadHold t m, MonadFix m) => (a -> b -> c) -> [a] -> Event t b -> m (Event t c)

который делает именно то, что я хочу, но не принимает событие как вход, а просто список. Учитывая, что у меня есть Event t [a]Я думал, что смогу создать событие, содержащее событие, и просто переключиться, но проблема в том, что zipListWithEven работает в монадическом контексте, поэтому я могу получить:

Event t (m (Event t a))

что-то, что switch Примитив не принимает.

Теперь, может быть, я подхожу к этому неправильно, поэтому вот моя общая проблема. Учитывая событие, которое генерирует список координат, и событие галочки, я хочу создать событие, которое я могу "использовать" для перемещения объекта по координатам. Таким образом, каждый раз, когда тик срабатывает, позиция обновляется. И каждый раз, когда я обновляю список координат, он начинает создавать позиции из этого нового списка.

2 ответа

Решение

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

trickle :: MonadMoment m => Event [a] -> Event () -> Event a
trickle eadd etick = do
    bitems <- accumB [] $ unions    -- 1
        [ flip (++) <$> eadd        -- 2
        , drop 1    <$  etick       -- 3
        ]
    return $ head <$> filterE (not . null) (bitems <@ etick) -- 4

Код работает следующим образом:

  1. Поведение bitems записывает текущие списки предметов.
  2. Элементы добавляются, когда eadd бывает...
  3. ... и один элемент удаляется, когда etick случается.
  4. Результатом является событие, которое происходит всякий раз, когда etick происходит, и он содержит первый элемент (ранее) текущего списка, когда этот список не пуст.

Это решение не требует каких-либо причудливых или запутанных рассуждений.

Называя части:

coords :: Event t [Coord]
ticks  :: Event t ()

Если мы хотим вспомнить самые последние Coord до следующего увольнения ticks, тогда мы обязательно должны быть в какой-то монаде Reflex m, Это монада, которая позволяет переходный процесс Event быть настойчивым.

Главное, что вы хотели бы запомнить, это стек Coord, Давайте попробуем это:

data Stack a = CS {
    cs_lastPop :: Maybe a
  , cs_stack   :: [a]
  } deriving (Show)

stack0 = CS Nothing []

pop :: Stack a -> Stack a
pop (CS _ []    ) = CS Nothing []
pop (CS _ (x:xs)) = CS (Just x) xs

reset :: [a] -> Stack a -> Stack a
reset cs (CS l _) = CS l cs

Там нет ничего реактивного, две функции, которые настраивают Stack Coord так, как вы упоминаете в своем вопросе.

Рефлекторный код для управления этим будет строить Dynamic t (Stack Coord), указав его начальное состояние и все, что его модифицирует:

  coordStack <- foldDyn ($) stack0 (leftmost [
      reset <$> coords
    , pop   <$  ticks
    ])

leftmost здесь берет список Stack Coord -> Stack Coord функции, которые применяются в свою очередь к stack0 от foldDyn ($) (пока coords а также ticks никогда не встречаться в одном кадре).

Вождение все это в main:

main :: IO ()
main = mainWidget $ do
  t0 <- liftIO getCurrentTime

  -- Some make up 'coords' data, pretending (Coord ~ Char)
  coordTimes <- tickLossy 2.5 t0
  coords <- zipListWithEvent (\c _ -> c) ["greg","TOAST"] coordTimes

  ticks <- tickLossy 1 t0

  coordStack <- foldDyn ($) stack0 (leftmost [
      reset <$> coords
    , pop   <$  ticks
    ])

  display coordStack
Другие вопросы по тегам