Targets · JavaScript · Keywords

Keywords

This page shows how each of Program's keywords becomes JavaScript. The output stays close to code you might write by hand: declarations become objects, classes and functions, and the reactive keywords become small calls into the runtime.

Contents

How a Program becomes JavaScript, in parts. Start with the introduction, then follow each part into the rendered output and the runtime it links against.

Program keywords in JavaScript

Each of Program's keywords becomes a small, familiar piece of JavaScript, one subsection below. The declarations turn into objects, classes and functions; the reactive keywords turn into short calls into the runtime. A keyword's inline modifier is shown as a subsection nested inside it.

Names pass through unchanged: a dotted Program name is emitted as written, because JavaScript objects nest to any depth. The only identifiers ever rewritten are ordinary variables and parameters whose name happens to be a JavaScript reserved word — see the Reserved words appendix.

MODULE

Each module becomes a nested object, so a dotted name is emitted exactly as written — JavaScript objects nest to any depth, so nothing is flattened.

var Code = {};
Code.Language = {};
Code.Language.Json = {};
// the Code.Language.Json.Value union is attached here

FUNCTION

A function renders to a JavaScript function: its INPUT and HOLE parameters become arguments in order, the body's bindings become statements, and the trailing = becomes a return.

function Wrap(body, decorate) {
  var decorated = decorate(body);
  return decorated;
}

INPUT

An input is a plain function parameter, in declaration order. A forward input is only read.

function FormSubmit(endpointName, formData) {
  // reads endpointName …
}

BINDING

The BINDING modifier makes the parameter two-way: the function both reads it and writes back through it, so a write reaches the caller's STATE. In JavaScript it stays the same parameter — the write-back happens through a REVERSE rather than a different signature.

function FormSubmit(endpointName, formData) {
  // formData is read AND written back
}

HOLE

A hole is a callback parameter, placed after the ordinary inputs. It is called where the caller's block should run, receiving that hole's own inputs as arguments.

function FromJsonArray(jsonArray, fromJson) {
  // fromJson(jsonElement) runs the caller's block per element
}

NATIVE

A native that returns a value is wrapped so it accepts reactive inputs: the guards run first, each input is unwrapped with Reactive.Value inside a Reactive.Calculate, and the verbatim js NATIVE string is spliced in as the body — so a native called with a loading input returns a loading value, for free.

Maths.Integer.ToString = function (value) {
  ReactiveSecurity.Gettable(value);
  var _value = value;
  return Reactive.Calculate(() => {
    var value = Reactive.Value(_value);
    return String(value);                // the verbatim js NATIVE body
  });
};

A native with no return value skips the Calculate wrapper and splices its body directly after the guards.

A record TYPE

A record renders to three generated pieces: a plain immutable class holding the fields, a constructor that lifts reactive or locked inputs, and one getter per field. The class and getters are generated machinery, so they live in a reserved _ slot no program name can reach (see naming considerations).

// the plain immutable shape (reserved) — one constructor arg and one property per FIELD
Point._ = class { constructor(x, y) { this.x = x; this.y = y; } };

// construction — the type name lifts plain, reactive or locked inputs through Calculate
Point = function (x, y) {
  ReactiveSecurity.Gettable(x);
  ReactiveSecurity.Gettable(y);
  return Reactive.Calculate(() => {
    var xValue = Reactive.Value(x);
    var yValue = Reactive.Value(y);
    return new Point._(xValue, yValue);
  });
};

// one generated getter per field (reserved)
Point._GetX = function (data) {
  return Reactive.GetObjectField(data, (d) => d.x, (d, v) => new Point._(v, d.y));
};
Point._GetY = function (data) {
  return Reactive.GetObjectField(data, (d) => d.y, (d, v) => new Point._(d.x, v));
};

Construction runs inside Reactive.Calculate, so a record built from a still-loading field is itself loading. Each getter runs through Reactive.GetObjectField, so reading a field of a locked record yields a locked field — and, when the record is a two-way binding, writing the field rebuilds the record and sets it back. Phase zero read fields only through these generated getters (Point._GetX(p)); Program also lets you write p.x directly, which lowers to the very same accessor (see the example).

A union TYPE

A union renders like a record: a tagged shape in the reserved _ slot, one constructor per OPTION, and one generated accessor per option. The constructors and accessors lift and read reactively, exactly as a record's do.

// the tagged shape (reserved)
Shape._ = class { constructor(tag, value) { this.tag = tag; this.value = value; } };

// one constructor per option — lifted through Calculate, like a record
Shape.circle = function (value) {
  ReactiveSecurity.Gettable(value);
  return Reactive.Calculate(() => new Shape._('circle', Reactive.Value(value)));
};
Shape.rect = function (value) {
  ReactiveSecurity.Gettable(value);
  return Reactive.Calculate(() => new Shape._('rect', Reactive.Value(value)));
};

// one generated accessor per option — the payload, or null for another shape
Shape._OptionalCircle = function (union) {
  return Reactive.Calculate(() => {
    var s = Reactive.Value(union);
    return s.tag === 'circle' ? s.value : null;
  });
};

Reading an option is locked-aware the same way. Phase zero read options only through these generated accessors (Shape._OptionalCircle(s)); Program also lets you write s.circle directly, projecting to the payload or, at depth one, a native null.

LET

A let is a single derived binding — one var per step of a computation, each feeding the next.

var squared = Maths.Real.Multiply(radius, radius);
var area = Maths.Real.Multiply(3.14159, squared);

STATE

A state is a reactive cell, created with Reactive.State.

var count = Reactive.State(0);

LOCKED

The LOCKED modifier passes a second argument of true to Reactive.State, so the cell starts locked — pending or carrying an error — rather than holding a settled value. A driver clears the lock when the real value arrives.

var responseState = Reactive.State(null, true);   // starts loading

REVERSE

A reverse is a write-back block. Its body computes from the incoming value, and the closing SET becomes Reactive.Set, pushing the result into an outer STATE.

function incrementFlag() {
  var currentCount = Maths.Integer.Increment(refreshCountState);
  Reactive.Set(refreshCountState, currentCount);   // SET
}

WITH

A with block swaps one module's implementation for another over its body, through Module.With, and restores the original binding when the block ends.

function Panel(content) {
  return Module.With(Logic, Ui.Logic, function () {
    return content();   // the hole, run with Logic = Ui.Logic
  });
}