Behaviors are ubiquitously defined as “time-varying value”s1.
Why? time being the dependency/parameter for varying values is very uncommon.
My intuition for FRP would be to have behaviors as event-varying values instead; it is much more common, much more simple, I wage a much more of an efficient idea, and extensible enough to support time too (tick event).
For instance, if you write a counter, you don't care about time/associated timestamps, you just care about the "Increase-button clicked" and "Decrease-button clicked" events.
If you write a game and want a position/force behavior, you just care about the WASD/arrow keys held events, etc. (unless you ban your players for moving to the left in the afternoon; how iniquitous!).
So: Why time is a consideration at all? why timestamps? why are some libraries (e.g. reactive-banana
, reactive
) take it up to the extent of having Future
, Moment
values? Why work with event-streams instead of just responding to an event occurrence? All of this just seems to over-complicate a simple idea (event-varying/event-driven value); what's the gain? what problem are we solving here? (I'd love to also get a concrete example along with a wonderful explanation, if possible).
1 Behaviors have been defined so here, here, here... & pretty much everywhere I've encountered.
Behavior
s differ from Event
s primarily in that a Behavior
has a value right now while an Event
only has a value whenever a new event comes in.
So what do we mean by "right now"? Technically all changes are implemented as push or pull semantics over event streams, so we can only possibly mean "the most recent value as of the last event of consequence for this Behavior
". But that's a fairly hairy concept—in practice "now" is much simpler.
The reasoning for why "now" is simpler comes down to the API. Here are two examples from Reactive Banana.
Eventually an FRP system must always produce some kind of externally visible change. In Reactive Banana this is facilitated by the reactimate :: Event (IO ()) -> Moment ()
function which consumes event streams. There is no way to have a Behavior
trigger external changes---you always have to do something like reactimate (someBehavior <@ sampleTickEvent)
to sample the behavior at concrete times.
Behaviors are Applicative
s unlike Event
s. Why? Well, let's assume Event
was an applicative and think about what happens when we have two event streams f
and x
and write f <*> x
: since events occur all at different times the chances of f
and x
being defined simultaneously are (almost certainly) 0. So f <*> x
would always mean the empty event stream which is useless.
What you really want is for f <*> x
to cache the most current values for each and take their combined value "all of the time". That's really confusing concept to talk about in terms of an event stream, so instead lets consider f
and x
as taking values for all points in time. Now f <*> x
is also defined as taking values for all points in time. We've just invented Behavior
s.