Reversibility
Reversibility is the idea that sets Reactive apart. Deriving a value from state is a forward calculation, read-only. A reactive value can also have a way back: a separate reverse function that takes a new result and sends it into the state behind it. Join the two and you have a value that reads forward and writes backward. Genuinely two-way, and a single declaration carries both directions.
Forward and reverse are separate
Forward and reverse are kept apart, because they are not always mirror images. A forward calculation might read
several inputs and combine them, and the way back could update just one of them, or none, or something else
entirely. Inverting an arbitrary calculation is unreliable, so you write the reverse yourself. You build the
forward direction by composing reactive operations, one per line, each a small
calculation; write the reverse as a separate Reactive.Reverse;
and join the two halves with Reactive.Binding.
const celsius = Reactive.State(Maths.Real)(20);
// forward: fahrenheit = celsius × 9 ÷ 5 + 32, one reactive step per line
const scaled = Maths.Real.Multiply(celsius, 9);
const ratio = Maths.Real.Divide(scaled, 5);
const reading = Maths.Real.Add(ratio, 32);
// reverse: still reactive code, so it composes with the same operations
const writing = Reactive.Reverse((f) => {
const offset = Maths.Real.Subtract(f, 32);
const widened = Maths.Real.Multiply(offset, 5);
const back = Maths.Real.Divide(widened, 9);
Reactive.Set(celsius, back);
});
// bind the two halves into one two-way value
const fahrenheit = Reactive.Binding(reading, writing);
Every line works on reactive values, keeping raw numbers out, even inside the reverse, which runs as reactive
code, so it reaches for Maths just like the forward direction. Reading
fahrenheit.value follows the forward chain. Calling Reactive.Set(fahrenheit, 212) runs
the reverse, which walks the operations back to 100 and sets celsius. The two
directions are described independently and bound together, so you tell Reactive exactly how to undo your
calculation. A readable half and a writable half are exactly what a reactive value
is made of.
Set flows backward
Reactive.Set is the whole write path, and it is polymorphic. Set a piece of state and the
value is stored directly. Set a derived or bound value and its reverse runs; because a reverse
typically calls Set on its own inputs, the edit cascades backward through the graph until
it reaches plain state at the roots.
Set a result
An edit targets a derived value, say a formatted or converted figure shown on the page.
Reverse runs
Its reverse function receives the new value and sets the inputs it was calculated from.
Cascade to state
Each of those sets may run a further reverse, until the change lands in the underlying state.
Interaction through bindings
This is why interaction in Reactive stays simple. A DOM interaction does exactly one thing: it
Sets a two-way binding to a new value, and the reverse cascade carries that change into state on its
own. Components expose bindings, and anyone can update them.
Interactions differ only in where the new value comes from:
A button
The value is already known in code: a flag, the next number, a chosen option. Pressing the button sets a binding to that value; the press only triggers the set.
An input
The value lives on the element. Binding an input's value to a settable makes it two-way: what is typed is written straight back through the reverse, and the binding is the wiring.
Why it matters
In most frameworks, two-way binding is stitched together by hand: a value flows out to the view, and a separate event handler you wrote flows the user's change back in. The two directions are unrelated code that you keep in sync yourself.
Reversibility collapses that into one declaration: describe how a value derives, and the way back
comes with it. The interaction model above is the same idea seen from outside; every event is a
Set, and the reverse does the rest.