我正在用Haskell在响应式(Reactive)香蕉中编写音乐播放器。我遇到的一个问题是使用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可以实现这种功能吗?
最佳答案
从react-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
创建的行为在更新时总是返回旧值(它们“落后一”),因为这对于递归定义非常有用。无论如何,的潜在问题是,在
addCmdEvent
出现之前,玩家位置已在外部进行了很长时间的更新,但是问题在于,这不是事件网络所看到的。相反,网络认为fromPoll
返回的行为与addCmdEvent
同时更新。实际上,除非您有权访问负责更新玩家位置的外部事件源,否则这是它唯一能想到的。 (如果您有权访问,则可以使用fromChanges
函数。)我意识到
fromPoll
的这种行为对于您的常见用例而言并不令人满意。但是,我不确定是否应该在我的库中对其进行修复:在fromPoll
返回最后一个值与changes
函数试图尽力而为之间存在一个权衡。如果返回了最新值,则changes
的行为就好像跳过了一个更新(当从外部更新该值时)并触发了一个多余的更新(当网络更新该值以匹配外部一个值时)。如果您对此有任何意见,请告诉我。请注意,将行为与适用的运算符
<*>
结合将可以很好地结合最新值。