Behavior in reactive-banana
Asked Answered
L

4

11

Pardon me, I'm just starting to look into reactive-banana and FRP.

The author of reactive-banana made this example per my suggestion, in which he creates a counter which can be increased and decreased. He uses accumE function which accumulates events. I think I was able to somewhat grok the Event type, and was able to test quite a few things with it, but then I remembered that there was also Behavior. I looked into it, but it seems like the behavior is meant to be used in similar situations; to modify an existing variable, just like accumE does with events.

What does Behavior mean, and what are the use cases for it?

Lingua answered 30/6, 2011 at 12:3 Comment(1)
T
6

Semantically, you have

Behavior a = Time -> a

That is, a Behavior a is a value of type a that varies over time. In general, you know nothing at all about when a Behavior a would change, so it turns out to be a rather poor choice for updating a text field on the click of a button. That said, it would be easy to get a behavior that expresses the current value of the number in the counter example. Just use stepper on the event stream, or alternatively, build it from scratch the same way, except by using accumB instead of accumE.

Typically, things you hook up to input and output will always be Events, so Behavior is used internally for intermediate results.

Suppose that in the given example, you want to add a new button that remembers the current value, like the memory function on simple calculators. You would start out by adding a memory button and a text field for the remembered value:

bmem    <- button f [text := "Remember"]
memory  <- staticText f []

You need to be able to ask for the current value at any time, so in your network, you'd add a behavior to represent it.

let currentVal = stepper 0 counter

Then you can hook up events, and use apply to read the value of the behavior every time the Remember button is pressed, and produce an Event with that sequence of values.

emem <- event0 bmem command
let memoryE = apply (const <$> currentVal) emem

And finally, hook up this new event to the output

sink memory [text :== ("", show <$> memoryE)]

If you wanted to use memory internally, then again you'd want a Behavior for its current value too... but since we only ever use it to connect it to an output, we only need an event for now.

Does that help?

Thecla answered 30/6, 2011 at 12:43 Comment(2)
Yes I believe this is exactly what I wantedLingua
Although perhaps misguided, as explained in my answer.Autocrat
A
10

I agree with Ankur rather than Chris: a text box is a value over time and so naturally wants to be a behavior rather than an event. The reasons Chris give for the less natural choice of event are implementation issues and so (if accurate) an unfortunate artifact of the reactive-banana implementation. I'd much rather see the implementation improved than the paradigm used unnaturally.

Besides the semantic fit, it's pragmatically very useful to choose Behavior over Event. You can then, for instance, use the Applicative operations (e.g., liftA2) to combine the time-varying text box value with other time-varying values (behaviors).

Autocrat answered 1/7, 2011 at 0:30 Comment(7)
Note that the value of a text box is inherently a discontinuous function of time, which I suspect is the cause of both the implementation issues and the perception that Behavior is less natural. This differs from, say, the position of a moving image on the screen, which is very naturally continuous. Would the derivative of the text box be a Behavior or an Event?Superscription
Do you really mean dis/continuous function of time, or function of dis/continuous time? Behaviors are about functions of continuous time, not continuous functions of time.Autocrat
Because it takes discrete values, and it's not meaningful to talk of changes in value occupying time spans. If the text box value over time is tb(t), and goes from tb(n) = "ab" to tb(n + 0.5) = "abc", the change is (++ "c") over the interval. If tb(n + 0.25) = X, the change from "ab" to X plus the change from X to "abc" should equal (++ "c"). There's no semantically meaningful way I can think of to divide (++ "c") in half, so the change must occur before or after X. Repeating the argument shows changes must have a duration of 0, making tb(t) a discontinuous function.Superscription
Oops, sorry, didn't see your edit to the comment. Yes, I meant a discontinuous function defined over continuous time, the "derivative" of which can only be sensibly interpreted as a function defined only for certain, discrete times. My impression is that's roughly what Event means but I could be wrong. The distinction I was trying to draw is that while any function defined over continuous time would be a Behavior, there's an intrinsic and meaningful difference between conceptually continuous vs. discontinuous functions.Superscription
Ah. So you're interested in continuous functions and more specifically differentiable functions. Which does not correspond to the original question of Behavior vs Event. Probably just a confusion around continuous time-functions vs continuous-time functions.Autocrat
By the way, consider the example of a time-varying string defined to continuously hold a representation of the value of a continuously changing real number (e.g. time, distance, temperature). And now suppose that string is the content of a text box.Autocrat
Yes, I've probably misunderstood the semantics of Event here to some extent, my apologies. I'm also starting to think that the perspective I'm coming from doesn't mesh with reactive-banana anyhow, so my remarks are probably irrelevant. Oh well.Superscription
T
6

Semantically, you have

Behavior a = Time -> a

That is, a Behavior a is a value of type a that varies over time. In general, you know nothing at all about when a Behavior a would change, so it turns out to be a rather poor choice for updating a text field on the click of a button. That said, it would be easy to get a behavior that expresses the current value of the number in the counter example. Just use stepper on the event stream, or alternatively, build it from scratch the same way, except by using accumB instead of accumE.

Typically, things you hook up to input and output will always be Events, so Behavior is used internally for intermediate results.

Suppose that in the given example, you want to add a new button that remembers the current value, like the memory function on simple calculators. You would start out by adding a memory button and a text field for the remembered value:

bmem    <- button f [text := "Remember"]
memory  <- staticText f []

You need to be able to ask for the current value at any time, so in your network, you'd add a behavior to represent it.

let currentVal = stepper 0 counter

Then you can hook up events, and use apply to read the value of the behavior every time the Remember button is pressed, and produce an Event with that sequence of values.

emem <- event0 bmem command
let memoryE = apply (const <$> currentVal) emem

And finally, hook up this new event to the output

sink memory [text :== ("", show <$> memoryE)]

If you wanted to use memory internally, then again you'd want a Behavior for its current value too... but since we only ever use it to connect it to an output, we only need an event for now.

Does that help?

Thecla answered 30/6, 2011 at 12:43 Comment(2)
Yes I believe this is exactly what I wantedLingua
Although perhaps misguided, as explained in my answer.Autocrat
A
5

Library author speaking. :-)

Apparently, Chris Smith can read minds because he accurately describes what I am thinking. :-)

But Conal and Arthur have a point, too. Conceptually, the counter is a value that varies in time, not a sequence of event occurrences. Thus, thinking of it as a Behavior would be more appropriate.

Unfortunately, behaviors do not come with any information about when they will change, the are "poll-only". Now, I could try to implement various clever schemes that will minimize the polling and thus allow effient updates of GUI elements. (Conal does something similar in the original paper.) But I have adopted a "no magic" philosophy: the library user shall be responsible for managing updates via events himself.

The solution I currently envision is to provide a third type besides Event and Behavior, namely Reactive (name subject to change) which embodies qualities of both: conceptually, it's a value that varies in time, but it also comes with an event that notifies of changes. One possible implementation would be

type Reactive a = (a,Event a)

changes :: Reactive a -> Event a
changes (_, e) = e

value :: Reactive a -> Behavior a
value   (x, e) = stepper x e

It is no surprise that this is precisely the type that sink expects. This will be included in a future version of the reactive-banana library.

EDIT: I have released reactive-banana version 0.4 which includes the new type, which is now called Discrete.

Aleris answered 4/7, 2011 at 14:55 Comment(8)
I'm sorry to see the semantic model and interface getting more complex for purely operational/implementation reasons.Autocrat
If you do stick with this new, implementation-driven type, I recommend against the name "Reactive", since the semantic inconsistency with my use in Push-pull functional reactive programming would likely lead to confusion.Autocrat
Basing UI updates directly on a value over time is arguably a conceptual mismatch anyway. Updates care about changes, not values. The motivation for making the distinction outlined in my comments on Conal's answer is that I've found many elements of a system have cleaner semantics when viewed as a sort of derivative. Actually applying the updates may further involve summing the derivative over the time since the last UI update, but this is a definite integral, and conceptually distinct from the original function.Superscription
I relate to "update" as purely an implementation issue, in the service of presentation of information that varies with continuous time. I would never use an implementation issue to guide or justify a semantic model or API. Rather, I always aim to keep the model & API as simple & precise as possible, and then figure out how to implement that simple model & API faithfully. In other words, I do my best to provide beautiful abstractions in spite of the clunky operational building blocks I have at hand (machine architectures, operating systems, low-level windowing libs, etc).Autocrat
In other words, I see update not as a notion intrinsic to UIs or to other time-varying phenomena, but rather as a secondary notion arising from wondering how to implement presentation of UIs (etc) on a class of machines and applying a class of imperative programming habits.Autocrat
@Conal: Agreed, and please note that I prefer viewing things in terms of changes rather than values for conceptual reasons first, because it lets the system be described directly in terms of its behaviors, without implicitly basing things off some absolute reference point. To be fair, I'm essentially arguing that introducing non-relative values for anything constitutes an implementation detail, which is rather extreme and probably incompatible with many existing notions of FRP.Superscription
I can understand appeal of a differential perspective, especially a continuous one, where it fits. I don't see a natural connection with the discreteness imposed by events. Maybe there are at least two (probably more) topics getting entangled here.Autocrat
@Conal: Very likely more, yes. :] Unfortunately, the comment system on SO actively discourages this sort of extended discussion, so I'll resist the temptation to elaborate further. That said, this has led me to more thoroughly consider some slightly nebulous ideas and helped me clarify how I think about the concepts, so I must thank you for your patience in replying here.Superscription
O
4

Generally, Behavior is a value that changes over a period of time. It is a continuous value, where as events are discrete values. In case of Behavior a value is always present. For example: The text on a text box is a Behavior as the text can change over a period of time but there will be a current value, where as a keyboard stroke in a event as you cannot query a keyboard stroke for its "current" value.

Oidea answered 30/6, 2011 at 12:35 Comment(2)
As I mentioned in my answer, behaviors are actually not a very good match for setting text fields in something like reactive-banana, since calls to the underlying graphics library functions to set the value need to happen at discrete times, so you actually need the event to let you know when the changes occur.Thecla
I'm with Ankur here, as reflected in my answer.Autocrat

© 2022 - 2024 — McMap. All rights reserved.