理想情况下,我希望拥有Behaviour
我可以"民意调查"得到当前时间。但是,使用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以发出警告事件。
当然,我可以并且会经常发起事件,所以我的警告不会大量错误。然而,它似乎确实是一个瑕疵,他们无法准确。
处理此问题的正确方法是什么?
答案 0 :(得分:2)
鉴于您的示例用例,我认为如果您远离fromPoll
,您应该没问题。为了解释原因,需要做一些澄清。 (注意:在下文中,“stream”指的是Event t a
,并且“发生”给组成它们的其中一个发射。)
但是,使用事件轮询行为(通过
<@
等)可以获得上一个事件的行为值,而不是当前值。
我想你是从stepper
的文档暗指这样的解释:
请注意,比较
timex < time
中的小于号表示行为的值会在事件发生后“略微改变”。这允许递归定义。
然而,该延迟仅针对用于定义行为的流(即传递给stepper
/ accumB
的流)以及与其同步的任何流。例如,假设您有两个独立的流eTick
和eTock
,以及以下网络代码段:
eIncrement = (+1) <$ eTick
bCount = accumB 0 eIncrement
eCountTick = bCount <@ eTick
eCountTock = bCount <@ eTock
eIncrement
和eCountTick
与eTick
同步,因此通过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
。