Given your use case, I think you should be fine if you don't stay away from fromPoll . To explain why a few clarifications are needed. (Note: hereinafter “flow” refers to Event ta and “occurrence” to one of the trade-offs that make them up.)
However, polling Behaviors with events (via <@ , etc.) gives me the Behavior value from the previous event, not the current value.
I assume you are referencing explanations such as this from the docs for stepper :
Note that a lower value in comparison with timex < time means that the behavior value changes “a little after” the events. This allows the use of recursive definitions.
This delay, however, only applies to the thread used to determine the behavior (i.e. the one you pass to stepper / accumB ), and any threads that are synchronized with it. For example, suppose you have two independent streams, eTick and eTock , as well as the following network fragment:
eIncrement = (+1) <$ eTick bCount = accumB 0 eIncrement eCountTick = bCount <@ eTick eCountTock = bCount <@ eTock
eIncrement and eCountTick are in sync with eTick , so the value observed through eCountTick is the "old" value; that is, the value before the synchronized update. However, from an eCountTock point of view, none of this matters. For an observer using eCountTock , there is no talk of delay, and the value is always current.
The behaviors that are observed from fromPoll cannot depend on themselves, therefore no cycles can be entered by observing the behavior immediately before this event, and not immediately after the previous event.
We are dealing only with threads synchronized with the one that updates the behavior. Thus, since the observed values ​​go “immediately before the next occurrence” and “immediately after the previous occurrence”, they come down to the same thing. fromPoll , however, confuses things a bit. It creates a behavior that is updated whenever an event in the event network occurs; and therefore updates are synchronized with the union of all threads. There is no such thing as a thread independent of the fromPoll event, and therefore the observed value will depend on the delay, but we observe it. Thus, fromPoll will not work for hours of application management, which requires some continuous change in tracking.
Implicit in all of the above is that a reactive banana does not have a built-in concept of time . Each thread has only “logical” timelines that can be intertwined by merging threads. Therefore, if we want the current behavior of time, our best bet is to create one of the independent flow. Here is a demonstration of this approach, which will give fresh and timely results, if the accuracy of threadDelay allows:
{-# 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 updated via eTime every 0.05 s; this is seen through eTick , a stream independent of eTime with occurrences every 5s. You can then use eTick and the streams received from it to monitor and update your objects. In addition, you can combine bTime and the behavior of the object in an applicative style to get, for example. Behavior for the latest pins to be observed using eTick .
Having canonical behavior over time looks like a sound approach in your case; it is conceptually clear and easy to generalize to several ticks. In any case, other approaches you can play with include getting rid of bTime and using eTick as a low-resolution current-time stream (although this seems to speed up the creation of broken threadDelay ) and eliminating eTick by using changes to Get a stream of fresh updated values ​​from behavior (thanks to which its own quirks and frustrations, as the documentation hints).