我正在Haskell写一个带有反应性香蕉的音乐播放器。我遇到的一个问题是使用fromPoll获取最新值。我想让用户可以选择在播放时选择音轨的一部分。我的代码看起来像这样:
makePlayNetworkDescr :: Player a => AddHandler Command -> a -> NetworkDescription t ()
makePlayNetworkDescr addCmdEvent player = do
bPosition <- fromPoll (getPosition player)
eCmds <- fromAddHandler addCmdEvent
let eSetStart = filterE (isJust) $ bPosition <@ filterE (==SetStart) eCmds
eSetEnd = filterE (isJust) $ bPosition <@ filterE (==SetEnd) eCmds
eClearRange = filterE (==ClearRange) eCmds
bStart = accumB Nothing ((const <$> eSetStart) `union` (const Nothing <$ eClearRange))
bEnd = accumB Nothing ((const <$> eSetEnd) `union` (const Nothing <$ eClearRange))
上面, getPosition 是一个部分函数,在播放实际开始之前返回 Nothing 。问题是,一旦 addCmdEvent 首次触发, bPosition 仍会保留 Nothing 值。 eSetStart / End 根据此计算其值。只有这样才能更新 bPosition ,这是下次 addCmdEvent 触发时将使用的值。依此类推,价值总是“一个一个”,可以这么说。
有一个相关的SO question,但在这种情况下,存在一个“触发器”事件,可用于计算行为的新值。 fromPoll有什么可能吗?
答案 0 :(得分:2)
从reactive-banana-0.5和0.6开始,只要外部事件触发事件网络,fromPoll
函数就会更新行为。您可以使用
eUpdate <- changes bSomeBehavior
但是,请注意,行为表示连续的时变值,这些值不支持“更新事件”的一般概念。 changes
函数将尝试返回有用的近似值,但没有正式的保证。
或者,您可以更改外部事件以将玩家位置包含在addCmdEvent
中。在您的情况下,这意味着向SetStart
和SetEnd
构造函数添加更多数据。然后,您可以使用
eSetStart = filterJust $ matchSetStart <$> eCmds
where
matchSetStart (SetStart pos) = Just pos
matchSetStart _ = Nothing
这两种解决方案都要求您将最近的值视为事件而不是行为。原因是使用stepper
创建的行为将始终在更新时返回旧值(它们“落后于1”),因为这对递归定义非常有用。
在任何情况下,潜在问题是玩家位置在addCmdEvent
发生之前很久就在外部更新,但问题是这不是事件网络看到的。相反,网络认为fromPoll
返回的行为与addCmdEvent
同时更新。事实上,除非您有权访问负责更新玩家位置的外部事件源,否则这是唯一可以思考的事情。 (如果您有权访问,则可以使用fromChanges
功能。)
我意识到fromPoll
的这种行为对于您的常见用例来说有些不尽如人意。我不确定是否应该在我的库中修复它:在fromPoll
返回最新值和changes
函数之间需要权衡以确保最佳状态。如果返回最新值,则changes
的行为就好像它已跳过一次更新(当值在外部更新时)并触发多余的值(当网络更新值以匹配外部值时)。如果您对此有任何意见,请告诉我。
请注意,将行为与应用运算符<*>
相结合,可以合并最近的值。