Modelling a spinner widget in RDP

31 views
Skip to first unread message

Manuel Simoni

unread,
Jan 18, 2018, 10:43:07 AM1/18/18
to reactive-demand
A spinner is a widget containing a numeric value that can be increased and decreased by 1 via the UI, e.g. by clicking on "plus" or "minus" buttons. Here's one somewhat satisfying model for spinners in RDP, driven by the want for having "command" objects that can be added to a history list for undo:

The behavior receives as input an asynchronous pair of a command signal and a data signal. Initially, i.e. after the behavior instance is initialized but before the user does something, the command signal will be a No-Op command. The data signal contains the current value of the widget from storage.

The output of the behavior is an asynchronous pair of a status signal and a data signal. Initially, the status signal will be a Ready status. The data signal passes through the value from the input signal.

When the user fires the "plus" button, the input command signal changes to a Plus-One command object. The behavior, upon noticing this, will output the increased-by-one value in its output data signal. This data signal is connected to a write behavior for the storage, that will write the new value.

Changing the value in storage will feed back to the behavior's input data signal, which is connected to a read behavior for the storage. Upon noticing the change of its input signal to the desired value, the behavior will set its status signal to a Done status.

To prevent timing-related errors, I'm including ETags, or rather versions, in all signals. This allows, e.g. to differentiate two different Plus-One command arriving in sequence.

I'd love to hear your thoughts on this scheme.
--Manuel

David Barbour

unread,
Jan 18, 2018, 12:48:49 PM1/18/18
to reactiv...@googlegroups.com
On Thu, Jan 18, 2018 at 9:43 AM, Manuel Simoni <msi...@gmail.com> wrote:
When the user fires the "plus" button, the input command signal changes to a Plus-One command object. The behavior, upon noticing this, will output the increased-by-one value in its output data signal. This data signal is connected to a write behavior for the storage, that will write the new value.

Usually a "plus-one" demand is a bit awkward, given demands have a continuous nature. However, I see you've tweaked this to a "plus-one when ready", then you change state from "ready" to "done". I suppose this could work, but it still seems a bit awkward, especially given something you mentioned just a little bit later:


To prevent timing-related errors, I'm including ETags, or rather versions, in all signals. This allows, e.g. to differentiate two different Plus-One command arriving in sequence.

Although I didn't contemplate spinner widgets specifically, one of my standard use cases when contemplating new/alternative paradigms has been voting systems. I think a spinner can be cast as a sort of voting system where an agent can vote multiple times. This would also generalize to multiple agents.

Assuming you have an e-tag per "plus-one" command, so it's essentially a `Vote ETAG` demand. In this case, it's trivial to simply inject the ETAG into a set representing the current state of our vote or spinner. Adding an element to a set is an idempotent operation, so it naturally works when continuously applied. The response to this demand could simply be an `Acknowledge ETAG`, providing some useful feedback that the specific vote has been acknowledged. 

The spinner widget, then, would simply demand the cardinality for the set and render it.

Conceptually and semantically, this is a lot simpler than dealing with destructive updates! However, we do need to consider space costs.

Rather than "undo" a vote by removing it from the set, an alternative is to keep two sets - one for upvotes, another for downvotes - then subtract cardinality of the former from the latter before rendering. This separation allows us to leverage set reduction mechanisms that "forget" the specific ETAGs recorded. I'll suggest two such mechanisms below:

First, we could simply include a timestamp with our ETAG, and reject votes that are too "old" according to the timestamp. Then we could use a frame-buffering mechanism, such that our state is partitioned into multiple sets and we record votes into an appropriate set based on timestamp. The oldest 'frames' are summarized by accumulating cardinality of votes, allowing us to gradually forget the specific set of tags. This design might allow temporary undo for recent votes.

Second, if you're okay with "fuzzy" counting and perhaps dropping the true undo feature in favor of two positive counts, you can leverage techniques similar to bloom filters to count a huge set of distinct inputs in a very small space. See "Big Data Counting: How To Count A Billion Distinct Objects In Only 1.5KB Of Memory" [1].

A set-based record-of-input models work pretty well with RDP due to the idempotent nature of pushing unique elements into a set. It is almost never a problem for our state resources to gradually summarize historical inputs to save space. And for many use cases, such as users posting messages on a forum, it isn't even a problem to just keep the full set of inputs. A lot of input modeling ideas from functional relational programming (the other FRP, cf. Mosely's "Out of the Tarpit" [2]) may thus be readily applied in context of RDP.

-- Dave



David Barbour

unread,
Jan 18, 2018, 1:17:59 PM1/18/18
to reactiv...@googlegroups.com
On Thu, Jan 18, 2018 at 11:48 AM, David Barbour <dmba...@gmail.com> wrote:
First, we could simply include a timestamp with our ETAG, and reject votes that are too "old" according to the timestamp. Then we could use a frame-buffering mechanism, such that our state is partitioned into multiple sets and we record votes into an appropriate set based on timestamp. The oldest 'frames' are summarized by accumulating cardinality of votes, allowing us to gradually forget the specific set of tags. This design might allow temporary undo for recent votes.

Also, this doesn't necessarily need to be a "time" stamp. It could instead be some form of unforgeable FrameID provided on demand, or perhaps a behavior referencing a subordinate resource to which we can apply the vote demand (via dynamic behaviors). Either of these could give us some extra capability security and allow more precise control of timeouts.

Manuel Simoni

unread,
Jan 18, 2018, 2:37:05 PM1/18/18
to reactiv...@googlegroups.com
I'm not sure I see the benefit of the voting idea. It quickly breaks down for similar widget types, such as a checkbox.

--
You received this message because you are subscribed to the Google Groups "reactive-demand" group.
To unsubscribe from this group and stop receiving emails from it, send an email to reactive-demand+unsubscribe@googlegroups.com.
For more options, visit https://groups.google.com/d/optout.



--
Manuel Simoni, Software Engineering Consultant
msi...@gmail.com | Tel: +43 (0)664 346 5158 | Skype: manuelsimoni

David Barbour

unread,
Jan 18, 2018, 4:53:02 PM1/18/18
to reactiv...@googlegroups.com
Recording a set of inputs very simple and general. It works in all cases. For a text area, you keep a computer history of edits. For a button, a complete history of press timestamps. For a checkbox, a complete history of toggles. The only problem with this is storage costs, which are unbounded. So we like to specialize when feasible.

In recent years, I've been thinking about programs themselves as UI. We can use projectional editing, for example, to render literal "true" as a checked box. And if we uncheck it, the underlying literal becomes "false". A text area is bound to a string literal. A radio button might be based on an enumeration. 

But if user gestures correspond to editing source code, then what are buttons?

Well, we could have automatic buttons in some cases. Like, a color literal could have a button to open a color picker. An integer literal could automatically be given a spinner. Or this might be driven by comment or annotation. But a "general" button could easily correspond to a literal list of timestamps. We render the button in place of the list, then extend the list whenever the button is pressed. We can compute application state from this history.

Alternatively, in a term rewriting language, a button could correspond to a thunk that is applied inline upon pressing. In this case, pressing a button would delete the button, but might create a new button in its place due to fixpoint behavior. This appeals to my "code as material" view - injecting an event can literally change the code.

Anyhow, I've drifted a bit but I feel you're underestimating the utility and generality of recording a history of input. I recommend you grasp the most general approaches before specializing and optimizing for more trivial states.


On Thu, Jan 18, 2018, 1:37 PM Manuel Simoni <msi...@gmail.com> wrote:
I'm not sure I see the benefit of the voting idea. It quickly breaks down for similar widget types, such as a checkbox.

On Thu, Jan 18, 2018 at 6:17 PM, David Barbour <dmba...@gmail.com> wrote:
On Thu, Jan 18, 2018 at 11:48 AM, David Barbour <dmba...@gmail.com> wrote:
First, we could simply include a timestamp with our ETAG, and reject votes that are too "old" according to the timestamp. Then we could use a frame-buffering mechanism, such that our state is partitioned into multiple sets and we record votes into an appropriate set based on timestamp. The oldest 'frames' are summarized by accumulating cardinality of votes, allowing us to gradually forget the specific set of tags. This design might allow temporary undo for recent votes.

Also, this doesn't necessarily need to be a "time" stamp. It could instead be some form of unforgeable FrameID provided on demand, or perhaps a behavior referencing a subordinate resource to which we can apply the vote demand (via dynamic behaviors). Either of these could give us some extra capability security and allow more precise control of timeouts.

--
You received this message because you are subscribed to the Google Groups "reactive-demand" group.
To unsubscribe from this group and stop receiving emails from it, send an email to reactive-dema...@googlegroups.com.

For more options, visit https://groups.google.com/d/optout.



--
Manuel Simoni, Software Engineering Consultant
msi...@gmail.com | Tel: +43 (0)664 346 5158 | Skype: manuelsimoni

--
You received this message because you are subscribed to the Google Groups "reactive-demand" group.
To unsubscribe from this group and stop receiving emails from it, send an email to reactive-dema...@googlegroups.com.
Reply all
Reply to author
Forward
0 new messages