Переключение на событие, заключенное в монадический контекст
Моя конкретная проблема такова:
Учитывая 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
Код работает следующим образом:
- Поведение
bitems
записывает текущие списки предметов. - Элементы добавляются, когда
eadd
бывает... - ... и один элемент удаляется, когда
etick
случается. - Результатом является событие, которое происходит всякий раз, когда
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