7

我正在用反应香蕉在 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 可以实现类似的事情吗?

4

1 回答 1

2

从 reactive-banana-0.5 和 0.6 开始,fromPoll只要外部事件触发事件网络,该函数就会更新行为。您可以通过使用将这些更新作为事件访问

eUpdate <- changes bSomeBehavior

但是,请注意,行为表示不支持“更新事件”的一般概念的连续时变值。该changes函数将尝试返回一个有用的近似值,但没有正式的保证。

或者,您可以更改外部事件以将玩家位置作为addCmdEvent. 在您的情况下,这意味着向SetStartandSetEnd构造函数添加更多数据。然后,您可以使用

eSetStart = filterJust $ matchSetStart <$> eCmds
    where
    matchSetStart (SetStart pos) = Just pos
    matchSetStart _              = Nothing

这两种解决方案都要求您将最近的值作为事件而不是行为来观察。原因是使用创建的行为stepper将始终在更新时返回旧值(它们“落后一个”),因为这对于递归定义非常有用。

无论如何,根本问题是玩家位置在addCmdEvent发生之前很久就在外部更新,但问题是这不是事件网络所看到的。相反,网络认为fromPoll更新返回的行为与addCmdEvent. 事实上,除非你有权访问负责更新玩家位置的外部事件源,否则它只能这么想。(如果您有访问权限,则可以使用该fromChanges功能。)

我意识到这种行为fromPoll对于您的常见用例来说有些不令人满意。fromPoll不过,我不确定是否应该在我的库中修复它:在返回最新值和changes试图做到最好的函数之间需要权衡。如果返回最新的值,那么changes它将表现得好像它跳过了一次更新(当值被外部更新时)并触发了多余的更新(当网络更新值以匹配外部更新时)。如果您对此有任何意见,请告诉我。


请注意,将行为与应用运算符<*>结合起来可以很好地结合最新的值。

于 2012-05-29T12:16:37.870 回答