How do you deal with the current time in reactive-banana?
Ideally I'd like to have a Behaviour
which I can "poll" to get the current time. However, polling Behaviour
s with Event
s (via <@
etc.) gives me the value of the Behaviour
from the previous Event
, not the current value. (I realise this is to avoid cyclic definitions which is indeed useful.)
I found fromPoll
which I thought would help. Behaviour
s that are observed from fromPoll
cannot depend on themselves, thus no cycles can be introduced by observing the behaviour just before this Event
is fired rather than just after the previous Event
fired.
A digression
In somewhat more formal terms I am suggesting that Event
s always occur at time t+ and Behaviours
are always observed at time t- i.e. Event
s observe behaviours that happen an infinitessimally short time before them. New values of Behaviour
s generated by accumB
and friends would always start from time t+ so could not be observed by Event
s which also happen at time t+.
Under this proposed semantics Behaviour
s created by fromPoll
would be updated just before each Event
is processed. Other Behaviour
s would be updated afterwards because they are created by accumB
and friends.
My use case
Anyway, that's a significant digression to my main question. I want to know if there's some way to deal with current time (not the time of the previous Event
) in reactive-banana. My use case is, for example, to keep track of the pings that entities send and if any of them hasn't sent a ping in a particular time interval to signal a warning event.
Of course I can and will fire off events very frequently, so my warnings won't be incorrect by a large amount. However it does seem to be a wart that they cannot be precise.
What's the right way of dealing with this?
Given your example use case, I think you should be fine if you stay away from
fromPoll
. To explain why, a few clarifications are needed. (Note: in what follows, "stream" refers to anEvent t a
, and "occurrence" to one of the firings which compose them.)I suppose you are alluding to explanations such as this one, from the docs for
stepper
:That delay, however, is only with respect to the stream used to define the behaviour (i.e. the one you pass to
stepper
/accumB
) and any streams that are synchronised with it. For instance, suppose you have two independent streams,eTick
andeTock
, and the following network snippet:eIncrement
andeCountTick
are in sync witheTick
, and so the value observed througheCountTick
is the "old" value; that is, the value before the synchronised update. From the point of view given byeCountTock
, however, none of that matters. To an observer usingeCountTock
, there is no delay to speak of, and the value is always the current one.We are only concerned with streams synchronised with the one which updates the behaviour. Thus, as far as observed values go "just before the next occurrence" and "just after the previous occurrence" boil down to the same thing.
fromPoll
, however, muddles things quite a bit. It creates a behaviour which is updated whenever any occurrence happens in the event network; and so the updates are synchronised with the union of all streams. There is no such thing as a stream independent from afromPoll
event, and therefore the observed value will be affected by the delay however we observe it. That being so,fromPoll
won't work for an application-driving clock, which requires tracking continuous change with some accuracy.Implicit in all of the above is that reactive-banana has no built-in notion of time. There are only the "logical" time lines within each stream, which can be interwoven by merging streams. So if we want a current time behaviour our best bet is building one from an independent stream. Here is a demo of that approach, which will produce fresh and timely results as far as the precision of
threadDelay
allows:bTime
is updated througheTime
each 0.05s; it is observed througheTick
, a stream independent fromeTime
with occurrences every 5s. You can then useeTick
and streams derived from it to observe and update your entities. Alternatively, you can combinebTime
and the entity behaviours in applicative style to get, e.g. behaviours for the latest pings, to be observed witheTick
.Having a canonical time behaviour looks like a sound approach in your case; it is conceptually clear and readily generalisable for multiple ticks. In any case, other approaches that you can play with include getting rid of
bTime
and usingeTick
as a low-resolution current time stream (though that seems to make thethreadDelay
innacurcies build up faster), and getting rid ofeTick
by usingchanges
to get a stream of freshly updated values from the behaviour (through that comes with its own quirks and annoyances, as the documentation hints at).