切换到包裹在 monadic 上下文中的事件

Switching to an event wrapped in monadic context

我的具体问题是这样的:

给定一个 Event t [a] 和一个 Event t ()(假设它是一个滴答事件),我想生成一个 Event t a,即一个给我连续项目的事件来自每次出现的滴答事件的输入列表。

Reflex 有以下助手:

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

这正是我想要的,但不将事件作为输入,而只是一个列表。鉴于我有一个 Event t [a],我想我可以产生一个包含事件的事件并只是切换,但问题是 zipListWithEven 在 monadic 上下文中运行,因此我可以得到:

Event t (m (Event t a))

这是 switch 原语不接受的东西。

现在,也许我以错误的方式接近它,所以这是我的一般问题。给定一个生成坐标列表和滴答事件的事件,我想生成一个可以 "use" 沿坐标移动对象的事件。所以每次 tick 触发时,位置都会更新。每次我更新坐标列表时,它都会从新列表中生成位置。

我不确定我是否正确理解了你想要的函数的语义,但在 reactive-banana 库中,我会这样解决问题:

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 的下一次触发,那么我们必须在 some monad Reflex m 中。这是允许瞬态 Event 持久化的 monad。

你想记住的核心是一堆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函数列表,通过foldDyn ($)依次应用到stack0(只要coordsticks 永远不会出现在同一帧中).

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