Runtime

Reactive.Runtime is what a driver calls. A program uses Reactive.State, Reactive.Reverse, Reactive.Match, and the rest of the application surface. The calls below are the ones a driver needs to register its types and holes, to see locks, to keep a child scope, and to write a value back.

Driver

Reactive.Runtime.Driver(name, body) builds the instance you pass to Reactive.Run. The name is the context. The body registers that context's types and holes.

const maths = Reactive.Runtime.Driver("Maths", () => {
  Reactive.Runtime.NativeType(Maths.Integer.Number, { default: 0 });
  Reactive.Runtime.Simple("Add", (left, right) => left + right);
});
One driver, one context.

NativeType

Reactive.Runtime.NativeType(type, options) registers a native type on the context. options.default is the value, or a function that returns it. options.parse is optional. The parser receives source text and returns the value. A missing field of this type uses the default.

Simple

Reactive.Runtime.Simple(name, calculation) implements a hole that finishes in the call. The calculation receives the raw values of the inputs, in order, and returns the raw result. The runtime applies the read lock and the write lock. Maths and strings use this.

Full

Reactive.Runtime.Full(name, body) implements a hole that sees the locks, or writes a value later. The body receives each argument as a boxed value. If, Each, a network request, and a control use this.

Value

Reactive.Runtime.Value(boxed) returns the raw value inside a boxed argument. A full hole uses it when it needs the current contents. The raw value of a native type is the value that driver stored.

Locks

Reactive.Runtime.Locks(boxed) returns the read lock and the write lock. The read lock is a flag. The write lock is a counter. The program does not receive this. A driver that finds the write lock set chooses to cancel the root process, or to keep its own queue and call Set when the lock is clear.

Set

Reactive.Runtime.Set(target, value) writes a state or a reverse the hole was given. One write is one refresh. A write of state stores that state. A write of a reverse starts a reverse process. A call that belonged to a reverse process which has finished is not made again.

Cancel

Reactive.Runtime.Cancel(target) cancels the root process that holds that value. The locks release and nothing is stored. A participating reverse is left in place. The driver then calls Set if it still has a new value.

Probe

Reactive.Runtime.Probe(arm) runs the arm and returns the gather objects it emitted into. The arm stores no state and sets no reverse. A read-locked If or Each probes each arm, locks those gathers, and takes no arm. Nested If and Each drivers see the probe and probe their own arms.

const gathers = Reactive.Runtime.Probe(arm);
The list is the gathers that already existed and were emitted into.

Enter and Absent

Reactive.Runtime.Enter(body) runs the body in the child scope of the current If slot. The next run finds the same scope. Reactive.Runtime.Absent() leaves that scope unused. The If driver calls one of them after it has read the condition. A read-locked condition calls neither, and calls Probe instead.

Each

Reactive.Runtime.Each(elements, body) keeps one child scope per element. elements is the list of stable ids and values the driver read from its array. The runtime matches the scopes it held last time by those ids, runs body for each id that is present, and drops a scope whose id is gone.

Id

Reactive.Runtime.Id() allocates a stable id. The id is not a slot in the program. The driver stores it in its own map, for example from the id to a DOM node. The runtime does not keep that map.

After

Reactive.Runtime.After(hook) runs the hook when the refresh finishes. The hook receives the ids this driver's holes produced this time. The driver reconciles there and clears its list for the next run.