处理活性香蕉中的当前时间

时间:2014-06-10 19:37:55

标签: haskell frp reactive-banana

你如何处理反应性香蕉的当前时间?

理想情况下,我希望拥有Behaviour我可以&#34;民意调查&#34;得到当前时间。但是,使用Behaviour(通过Event等)对<@进行投票,可以从上一页 {{1}中获取Behaviour的值},而不是当前值。 (我意识到这是为了避免确实有用的循环定义。)

我发现Event我认为会有所帮助。从fromPoll观察到的Behaviour不能依赖于他们自己,因此在这个fromPoll被触发之前观察行为而不是< em>刚刚之前的Event被解雇。

一个题外话题

在一些更正式的术语中,我建议Event总是在 t + 时发生,Event总是在时间 t - 时观察到即Behaviours观察在他们面前无限短时间内发生的行为。由Event和朋友生成的Behaviour s的新值始终从 t + 开始,因此accumB s无法观察到这些值 > T +

在此Event下创建的语义Behaviour将在每个fromPoll处理之前更新。之后会更新其他Event,因为它们是由Behaviour和朋友创建的。

我的用例

无论如何,这对我的主要问题来说是一个重大的题外话。我想知道是否有某种方法可以处理当前时间(不是上一个 accumB的时间)在reactive-banana中。例如,我的用例是跟踪实体发送的ping,以及它们中的任何一个是否在特定时间间隔内发送ping以发出警告事件。

当然,我可以并且会经常发起事件,所以我的警告不会大量错误。然而,它似乎确实是一个瑕疵,他们无法准确。

处理此问题的正确方法是什么?

1 个答案:

答案 0 :(得分:2)

鉴于您的示例用例,我认为如果您远离fromPoll,您应该没问题。为了解释原因,需要做一些澄清。 (注意:在下文中,“stream”指的是Event t a,并且“发生”给组成它们的其中一个发射。)

  

但是,使用事件轮询行为(通过<@等)可以获得上一个事件的行为值,而不是当前值。

我想你是从stepper的文档暗指这样的解释:

  

请注意,比较timex < time中的小于号表示行为的值会在事件发生后“略微改变”。这允许递归定义。

然而,该延迟仅针对用于定义行为的流(即传递给stepper / accumB的流)以及与其同步的任何流。例如,假设您有两个独立的流eTickeTock,以及以下网络代码段:

eIncrement = (+1) <$ eTick
bCount = accumB 0 eIncrement
eCountTick = bCount <@ eTick

eCountTock = bCount <@ eTock

eIncrementeCountTickeTick同步,因此通过eCountTick观察到的值是“旧”值;也就是说,同步更新之前的值。然而,从eCountTock给出的观点来看,这些都不重要。对于使用eCountTock的观察者来说,没有延迟可以说,而且值始终是当前值。

  

fromPoll观察到的行为不能依赖于他们自己,因此在此事件被触发之前仅观察行为而不是之后上一次事件被解雇。

我们只关心与更新行为的流同步的流。因此,只要观察到的值“恰好在下一次出现之前”和“就在上一次出现之后”归结为相同的事物。然而,fromPoll使事情变得相当混乱。它会创建一个在事件网络中发生任何事件时更新的行为;所以更新与所有流的并集同步。没有独立于fromPoll事件的流这样的东西,因此观察到的值将受到延迟的影响,但我们观察到它。既然如此,fromPoll将无法用于应用程序驱动时钟,这需要以一定的准确度跟踪连续变化。

以上所有内容都隐含reactive-banana has no built-in notion of time。每个流中只有“逻辑”时间线,可以通过合并流交织。因此,如果我们想要当前时间行为,我们最好的选择是从独立流构建一个。以下是该方法的演示,只要threadDelay的精度允许,就会产生新的和及时的结果:

{-# LANGUAGE RankNTypes #-}
module Main where

import Control.Concurrent
import Control.Monad
import Data.Time
import Reactive.Banana
import Reactive.Banana.Frameworks

main = do
    let netDesc :: forall t. Frameworks t => Moment t ()
        netDesc = do
            (eTime, fireTime) <- newEvent
            liftIO . forkIO . forever $
                threadDelay (50 * 1000) >> getCurrentTime >>= fireTime
            bTime <- flip stepper eTime <$> liftIO getCurrentTime
            (eTick, fireTick) <- newEvent
            liftIO . forkIO . forever $
                threadDelay (5000 * 1000) >> fireTick ()
            reactimate $ print <$> bTime <@ eTick
    network <- compile netDesc
    actuate network >> threadDelay (52000 * 1000) >> pause network

bTime每{0.05}更新一次eTime;通过eTick观察到一个独立于eTime的流,每5秒出现一次。然后,您可以使用eTick和从中派生的流来观察和更新您的实体。或者,您可以将bTime和应用样式中的实体行为结合起来,例如使用eTick观察最新ping的行为。

在您的情况下,具有规范时间行为看起来像是一种合理的方法;它在概念上是清晰的,并且易于通用于多个滴答。在任何情况下,您可以使用的其他方法包括摆脱bTime并使用eTick作为低分辨率的当前时间流(尽管这似乎会使threadDelay无价格成为现实通过使用changes从行为中获取新更新的值流(通过它自身的怪癖和烦恼,如文档提示),摆脱eTick