【问题标题】:Switching to an event wrapped in monadic context切换到包装在单子上下文中的事件
【发布时间】:2016-11-27 08:42:57
【问题描述】:

我的具体问题是这样的:

给定一个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 在单子上下文中运行,因此我可以得到:

Event t (m (Event t a))

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

现在,也许我以错误的方式接近它,所以这是我的一般问题。给定一个生成坐标列表和刻度事件的事件,我想生成一个可以“使用”沿坐标移动对象的事件。因此,每次触发滴答声时,都会更新位置。每次我更新坐标列表时,它都会开始从该新列表中生成位置。

【问题讨论】:

    标签: frp reflex


    【解决方案1】:

    我不完全确定我是否正确理解了您想要的函数的语义,但是在响应式香蕉库中,我会这样解决问题:

    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 发生时就会发生一个事件,并且只要该列表不为空,就会包含(先前)当前列表的第一个元素。

    这个解决方案似乎不需要任何花哨或复杂的推理。

    【讨论】:

    • 这不是我想要的(它会累积传入列表,而我想在每次新到达时替换坐标源),但我认为这没有显着差异,它显示了方法。我现在想我知道我的思维问题是什么了,我一到电脑前就会试着把它翻译成反射,谢谢!
    • 是的,稍作调整! FRP 语义很常见,所以不使用特定库的人会有所帮助:)
    【解决方案2】:

    命名部件:

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

    如果我们想记住最近的Coord 直到下一次触发ticks,那么我们必须在一些单子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 -&gt; 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
    

    【讨论】:

    • 我喜欢它从普通的数据类型/函数开始,然后在一个表达式中使其反应。很高兴记住这一点,考虑到 FRP 似乎是一种范式转变,以至于很容易迷失在其错综复杂的事物中,而不是保持冷静并只是编写功能代码;)
    猜你喜欢
    • 2022-08-19
    • 1970-01-01
    • 2018-05-04
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多