Targets · JavaScript · Example
Example
The whole language in one short program — a module with a record, a tagged union, a function with per-platform native bodies, and a function that wires up state, a write-back and a return, then a block of global code that ties them into a small running application with a user interface — and the complete JavaScript it renders to, down to the run call and its drivers. The module is the one shown on the language page.
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.
- Introduction — what the JavaScript target is, and how to read the rest of this section.
- Capabilities — which high-level features are possible, built in or through a library.
- Coding standards — the Frenzi coding standards the generated code follows, global and JavaScript-specific.
- Example — a full program using every keyword, and the JavaScript it renders to.
- Naming considerations — how Program's dotted names are carried into JavaScript.
- Entry point — the generated
main()that wires up the drivers and starts the reactive cycle. - Drivers — the native drivers that provide capabilities like the user interface and animation, draining each cycle's emissions.
- Runtime — the Reactive and Context machinery the rendered code links against.
- Native modules — the hand-written, per-platform JavaScript behind
NATIVEfunctions. - Keywords — how each of Program's keywords renders to JavaScript.
- Project setup — the files and commands to build and run the rendered program.
- Reserved words — the words JavaScript keeps for itself, and how a Program name that collides with one is escaped.
Every keyword, in one program
Each keyword has its own page under keywords; here they are all
together — the MODULE declaring the pieces, then the global code beneath it that
runs them as an application: a profile card that greets by name, shows a welcome row, and has a
button that ages the profile up.
MODULE App
TYPE Profile
FIELD Text name
FIELD Maths.Integer age
TYPE Status
OPTION App.Profile active
OPTION Text pending
FUNCTION Text Greet
INPUT Text name
NATIVE js "return 'Hi ' + name;"
NATIVE dart "return 'Hi ' + name;"
NATIVE swift "return \"Hi \" + name"
FUNCTION Text Welcome
INPUT BINDING App.Profile profile
HOLE Text row
INPUT Text line
STATE Maths.Integer count = 0
STATE LOCKED Network.Web.Response saved = null
REVERSE App.Profile submit
LET Network.Web.Response response = Network.Web.Client.Post profile
SET saved = response
LET Text label = Maths.Integer.ToString count
= label
STATE App.Profile user = App.Profile "Ada" 36
LET Text greeting = App.Greet user.name
Ui.Content.Title greeting
App.Welcome user
Ui.Content.Text line
Ui.Button.Integer.Increment user.age
Ui.Content.Text "Older"
Rendered JavaScript
The whole program, rendered. The module is a nested object; Greet's js
native body is spliced in; the function's inputs and holes are parameters, its state and write-back are runtime
calls, and the trailing = is a return. Records and unions are plain
immutable shapes in a reserved _ slot, each with a constructor that lifts its
inputs through the reactive runtime (see construction and field access).
At the foot, the global code becomes the run call,
Program._Runtime.Run — the root run body, then the
drivers the application needs.
var App = {};
// A record: a plain immutable shape in a reserved `_` slot, plus a constructor
// that lifts plain, reactive or locked inputs through Program._Runtime.Reactive.Calculate.
App.Profile = function (name, age) {
return Program._Runtime.Reactive.Calculate(() =>
new App.Profile._(Program._Runtime.Reactive.Value(name), Program._Runtime.Reactive.Value(age)));
};
App.Profile._ = class { constructor(name, age) { this.name = name; this.age = age; } };
// A union: one tagged shape, one constructor per option, lifted the same way.
App.Status = {};
App.Status.active = (value) => Program._Runtime.Reactive.Calculate(() => new App.Status._('active', Program._Runtime.Reactive.Value(value)));
App.Status.pending = (value) => Program._Runtime.Reactive.Calculate(() => new App.Status._('pending', Program._Runtime.Reactive.Value(value)));
App.Status._ = class { constructor(tag, value) { this.tag = tag; this.value = value; } };
App.Greet = function (name) {
return 'Hi ' + name; // the js NATIVE body
};
App.Welcome = function (profile, row) {
var count = Program._Runtime.Reactive.State(0);
var saved = Program._Runtime.Reactive.State(null, true);
Program._Runtime.Reactive.Reverse(submit, () => {
var response = Network.Web.Client.Post(profile);
Program._Runtime.Reactive.Set(saved, response); // SET
});
var label = Maths.Integer.ToString(count);
return label;
};
// the global code — the running application
Program._Runtime.Run(() => {
var user = Program._Runtime.Reactive.State(App.Profile('Ada', 36));
var _user_name = App.Profile._GetName(user); // field getter, made on first use
var greeting = App.Greet(_user_name);
Ui.Content.Title(greeting);
App.Welcome(user, (line) => { Ui.Content.Text(line); });
var _user_age = App.Profile._GetAge(user); // two-way accessor for user.age
Ui.Button.Integer.Increment(_user_age, () => { Ui.Content.Text('Older'); });
},
Ui.Dom.Driver('app'), // mount the interface into the page root
Network.Web.Driver(), // serve the request App.Welcome's submit makes
);
Construction and field access
The shapes above are only half the story. Every value in Program is reactive: an argument may be a plain value, a reactive value, or a locked (still-loading) reactive value, and anything derived from it must carry that state forward. Two moments need care — building a record or union, and reading or writing its fields.
Constructing — the type or option name
Calling a type name (App.Profile name age) or a union option
(App.Status.active profile) does more than new the shape. It pulls each
argument through Program._Runtime.Reactive.Value inside a Program._Runtime.Reactive.Calculate, so plain and
reactive inputs both work — and, crucially, if any input is locked the whole
constructed value is locked too:
App.Profile = function (name, age) {
return Program._Runtime.Reactive.Calculate(() =>
new App.Profile._(Program._Runtime.Reactive.Value(name), Program._Runtime.Reactive.Value(age)));
};
Program._Runtime.Reactive.Calculate remembers every value unwrapped with Program._Runtime.Reactive.Value
during its callback and, if one of them is locked, marks its own result locked. So a
Profile built from a still-loading age is itself loading until the age
arrives — the loading state propagates with no explicit checks. The
runtime page covers Calculate and
Value in full.
Reading a field — and writing it back
profile.name is a reactive, two-way projection. It lowers to a temporary that
calls the field's generated getter — one line, made on demand just before the field is
first read in a scope, and reused for every later read or write of it. The temporary is named for the
value and the field, with a leading underscore and another between them
(_profile_name), so it can never collide with a program name:
var _profile_name = App.Profile._GetName(profile); // made once, on first use
Ui.Content.Text(_profile_name);
The getter is generated onto the record's reserved _ slot —
TYPE shows how — and runs the read
inside a Calculate, so a locked profile yields a locked name.
When profile is a two-way binding the getter
returns a two-way accessor, so that same _profile_name temporary is also the write
target: Program._Runtime.Reactive.Set(_profile_name, next) rebuilds the record with the changed field and
stores it back. A field read and written in one scope therefore share a single temporary, and
nothing is mutated in place.
A union option reads the same way, through its generated
accessor — the payload, or a native null at depth one when the option is not set:
var _status_active = App.Status._OptionalActive(status); // status.active
Where the generated helpers live
Every generated name here carries an underscore, so none of it can clash with a name the program
defines: the raw immutable class and the getters sit on the record's reserved _ slot
(App.Profile._, App.Profile._GetName), and each on-demand temporary is
_-prefixed too (_profile_name). Since _ is illegal in a Program
identifier, user code can never name, call, or clash with any of it. That is the phase-one fix for a
phase-zero hazard, where a generated helper such as Profile.GetName shared the program's
own namespace and could collide.
The run call and its drivers
The global code at the foot of the program is the root — the run body the
runtime executes on every cycle. In the render above it is the first argument to
Program._Runtime.Run; everything after it is a
driver. This application needs two: the interface
driver, which mounts the emitted interface into a root element on the page, and the network driver,
which serves the request App.Welcome's
write-back makes.
The first argument is the program: run once, then re-run whenever a value it read is written
from outside. Each cycle the root emits interface descriptors into context channels and the drivers
drain them, reconciling the real DOM and network (see
drivers and
runtime). The Older button closes the
loop: Ui.Button.Integer.Increment is given the two-way user.age accessor and,
on click, increments it — rebuilding the profile with the new age and storing it — so the
next cycle re-renders the title and row from the updated state. The write-back itself lives inside the
button component, not in user code; a SET only ever
appears inside a REVERSE, as in
App.Welcome's submit above.
The entry point sits in the reserved Program._Runtime namespace — the same
underscore convention that walls off App.Profile._, so this runtime glue can never clash
with anything the program itself names. The friendlier
Program.Application(root, [drivers])
on the entry-point page is a thin public wrapper over exactly this call.
Reading it
Indentation becomes nested closures: the REVERSE body is
a () => { … } block, and any indented interface would nest the same way. The dotted
names pass straight through — Network.Web.Client.Post,
Maths.Integer.ToString — see
naming considerations. How each
keyword maps, one by one, is on keywords.