I don’t know how to characterize clearly the current Reactive implementation with regard to push vs pull. In a sense, it’s all push, and in another sense, it’s all pull. At each output, there’s a thread that repeatedly pulls individual values out of a reactive value (essentially a stream). Each pull blocks until a new value is ready. In this implementation, reactive values are built compositionally, each having its own thread, blocked most of the time. Each input simply provides a value, resulting in a cascading network of brief unblocking of other threads.

Thus the sequence of actions is very push-like, while the direction of pointers and code style is more like pull. I think it happens to find a happy combination of push-driven efficiency with the GC-friendliness of pull (because GC favors demand-driven computation).

Wow. Lazy evaluation rocks! Am I right that if we set all external sources first (e.g. set mouse & keyboard state at the begining of the frame) and will evaluate the scene after (i.e. render the frame) then we get synchronous evaluation for free thanks to lazyness?

I don’t think it’s quite that simple and automatic. The output end has to be a bit clever so that it doesn’t actually display (and thereby evaluate) every value that arrives. I don’t know how to wrap up that cleverness in a general and transparent way. In Phooey, I used an “idle” event per output. Every output (including intermediate values) gets stuffed into an `IORef`

but not displayed. On idle, the latest one *since the previous display* is pulled out and displayed. (The `IORef`

is `Maybe`

-valued and is cleared to `Nothing`

after each display.) If there is no new value since the previous display (the usual case), nothing happens. The display step is what actually forces evaluation. When previously stored but undisplayed values get overwritten, as with intermediate values or semantically valid rapidly changing values, the undisplayed values never get evaluated. I guess it’s a bit wasteful to repeatedly check that there’s still no new value to display.

Or more simply, if we set “event” from the example above first (not evaluating C yet) and then start evaluating C then all intermediate values (2+1) will be just skipped by mappend without calculating?

Not quite. `mappend`

passes along the intermediate and final values, and the cleverness to ignore them is in the display end.

Note that the implementation is not smart enough to know that it’s skipping values for different reasons (intermediate vs too frequent to evaluate and display). Maybe it could get smarter.

And if I understand correctly skipped thunks with lazy values will be created only on top-level value calculation, not at event setting phase (i.e. thunks generation is demand driven, which is very nice for GC). Or there is exists some eager (push) thunk generation in mkEvent internals (some threads are unpaused and MVars+threads are recreated bottom-up)?

I think I’ve answered this question above, as clearly as I understand.

As for examples of continuous values I’m most interested in the implementation of integral function (all other examples are just combinations on top of it). I feel that some switching plus start time keeping is needed here, but I don’t clearly see the possible implementation.

I haven’t thought about how to do integration in this setting, though it’s very important to me as well. Here’s are some thoughts, off the top of my head:

- Integrate piecewise, i.e., integrate each non-reactive time function, and keep a running sum of the results. As you noted, we have to know when the switch happens.
- Exploit the
`Fun`

representation to integrate exactly in the special case of constant functions. Extend the representation so additional cases can be integrated exactly. Perhaps univariate polynomials. - Choose a numeric integration method to use.
- Adapt the step size to available computation cycles. For instance, give each integral a thread that repeatedly takes a step and then
`yield`

s. Each time delta would be measured as the wake-up time minus the time of the previous step. - I wonder how integration threads would interact with my idle trick. Currently, my threads are all blocked most of the time, but the new integration threads yield while still runnable. Would the new active threads over-compete with the single GUI thread handling all of the outputs?

Or more simply, if we set “event” from the example above first (not evaluating C yet) and then start evaluating C then all intermediate values (2+1) will be just skipped by mappend without calculating?

And if I understand correctly skipped thunks with lazy values will be created only on top-level value calculation, not at event setting phase (i.e. thunks generation is demand driven, which is very nice for GC). Or there is exists some eager (push) thunk generation in mkEvent internals (some threads are unpaused and MVars+threads are recreated bottom-up)?

As for examples of continuous values I’m most interested in the implementation of integral function (all other examples are just combinations on top of it). I feel that some switching plus start time keeping is needed here, but I don’t clearly see the possible implementation.

]]>While my push implementation looks like it performs unnecessary recomputation, it needn’t, thanks to laziness. A single event occurrence may trigger thunks for multiple values may be generated for an output, but only the last one need get displayed and hence actually evaluated. The usage context must be a bit clever, as is the case in Phooey. (See `idler`

. I haven’t explained this point clearly in the docs, and I don’t really expect anyone to figure it out on their own.

By the way, even if the push implementation were recomputing unnecessarily, it would probably do so *much* less than a polling implementation, which recomputes even when no event or irrelevant events occur. Still, I like having no redundant computation at all.

The above remarks all apply to the implementation. Semantically, erroneous/inconsistent values (e.g., `2+1`

) do appear but with a time duration of zero, so if you integrate the error function, you’ll get zero total error.

Many uses of interaction use discrete approximations to continuous behavior, such as considering only finitely many mouse position samples or generating only finitely many display frames. In those cases, the intermediate, inconsistent-state values like `2+1`

may well be be as accurate as either of the two consistent-state values (fully before and fully after the event). (Any discrete implementation will have nonzero total error, but at least I won’t add any unnecessary error.)

I don’t have continuous (time-dependent) examples for Reactive yet. Until I do, you can see my old Fran tutorial to get the feel of what I have in mind.

]]>What I don’t understand yet is how synchronous compuations can be done with this model?

Take the following computation for example (I use CSP-like notation for simplicity):

A = 1 -> event -> 2 B = 1 -> event -> 3 C = A + B

In continuation-based polling FRP implementation (`data SF a b = SF { sfTF :: Time → a → (SF a b, b) }`

) A and B will switch simultaneously (C = 2 -> event -> 5). But in your pushing (event-driven) implementation C will become 2+1 after handling A and then 2+3 after handling B.

I.e. computations in your implementation are not synchronous. With this loss of synchronism we can get too much recalculations if several values depend from one event and there is a value which in turn depends on them. Plus we get some non-determinism in this case (at least with current implementation of mappend on Futures).

So what’s interesting to me — can the synchronous computations be implemented in Data.Reactive in some simple way?

Also I’m interested in some examples of using time-dependent Data.Fun reactive values. Are there any of them? (I didn’t find them in Examples.hs)

]]>