Concepts

This page defines the terms that the rest of the Reference uses for the model of a program. Each entry gives the definition, the rules that follow from it, and the nodes and builtins where the term matters. It is a set of definitions, not a tutorial.

Objects and values

An object is an instance of a schema: a unit of data that has a schema, its compile-time description (kind, layout and, for Code, signature), fixed for its whole life and always available at compile time, and a value, the data it currently holds. Objects exist as data-stack items, as locals (objects bound to a name within a scope, a region of processing with its own names), as Struct fields, and as targets of references. The schema decides which operations apply and what a write may supply; the value decides what operations produce, and an operation that needs a target is rejected when its target is NIL at compile time. A literal such as 5 or "text" creates an object with that value. A schema is always taken from an object, never written as an annotation: the Standard Library defines Int32, Cond, Real64 and the other type names so that mentioning one pushes a new object with the default value (Int32 pushes 0), and x: Int32; creates an Int32 local that way.

Meta objects

A schema is Meta exactly when its size is zero: its objects exist only at compile time and occupy no run-time storage. Blocks are Meta.

Block and Code

A Block is a Meta object holding unprocessed source nodes; [ ... ] creates one from the nodes between the brackets. A Block is not processed where it is written, and its source position is part of its schema, so two Blocks written in different places have different schemas. A Block has no environment of its own: the names it mentions are resolved where it is processed, against the scopes visible there within the Block's own module (n: 1; show: [n]; then [n: 2; show] call reads 2); a Block imported from another module sees that module's names, never the caller's locals.

Code is the schema kind of references to run-time functions; each Code schema carries a signature (inputs, output, options), and a non-NIL Code value is callable. Code values come from exportFunction, importFunction, and from writing a Block into a local or field of schema Code with !NAME, .!NAME, ! or fieldWrite (not set), which processes the Block once, as a function with that Code's signature; such a body cannot use the locals of the enclosing scope, except Meta locals and static ones. codeRef constructs the signature only and yields a Code object whose value is NIL, which cannot be called.

A Block captures the objects of enclosing scopes that it mentions or consumes: a non-static, non-Meta In-place local or a non-Meta In-place stack item through a view of the existing object, which adds no destruction (a managed item is destroyed once, by its own scope); a static local by its known value. Meta objects retain their Meta schemas; Ref-like locals and stack items use their stored references. A Code body compiles as a separate function and can use globals and enclosing Meta or static locals and fields; other enclosing locals and fields are invalid.

Structs

A Struct is one object whose items are objects, the fields, in a fixed order; a Dict also names them, a List and a Tuple do not. { ... } opens a scope and creates a Struct, normally a Dict, from the locals created inside, in creation order (the Dict node lists the unnamed cases). Objects left on the data stack inside the scope pass through to the enclosing stack and do not become fields; a scope creating no locals yields the empty Tuple (). ( ... ) collects the objects pushed inside into a List (equal item schemas) or a Tuple.

A Struct is uniform when all its fields have one schema — a List always, a Dict when its field schemas are identical, a Tuple never, a Tuple whose items share one schema being a List — and only a uniform Struct accepts an unknown ordinal in @, !, fieldRead and fieldWrite.

A Struct field is static when its value is packed into the Struct's schema, so it is part of the type and identical for every object of that schema. The virtual modifier marks the next created local as static; the local itself keeps an ordinary schema and can still be written, and its value is packed when the scope becomes a Struct. A field is solid when it occupies run-time storage; static and Meta fields do not, and a Struct whose fields are all static or Meta is itself Meta. schemaName shows a static field with its value and the word virtual, for example {a: Int32; b: 1 virtual;}.

Creating a static local requires a packable initial value: its schema is not managed, its value and every solid item of it are known, and every Ref or Code item is a known NIL.

Callable fields are methods: a NAME or .NAME mention of a field holding a Block calls it with the Dict's fields in scope, while @NAME and .@NAME read the Block without calling it. The field names INIT, DIE, ASSIGN, CALL, PRE, and SCHEMA_NAME are reserved: INIT runs, for a Dict that also has DIE, when a new object of the schema is constructed (new, array, newVarOfTheSameType), not when the literal is written; DIE runs at destruction; ASSIGN performs copies; CALL makes the Dict callable; PRE guards name lookup; a static SCHEMA_NAME supplies schemaName.

Locations, references, and views

An In-place object holds its value in its own storage; a Ref-like object (Ref, Code, Text) holds a reference to a value elsewhere. A view is a Ref to an In-place object through which that object is read or, if the view is mutable, also written; an immutable view permits reading only. The complete NAME/@NAME and member-selection rule is defined in Selection and destinations. Immutability belongs to the view, not the object: const and unconst change only the view on the stack, and other references to the object are unaffected. Most operations dereference, that is, act on the target of a view instead of the view (x 1 + adds the referenced number, same compares dereferenced schemas). A write replaces the value of a destination with that of a source; !NAME does not dereference its source: for an In-place local it stores a value of the local's schema, so a view as source is rejected (x !y fails, x new !y writes a copy); for a Ref local it binds a new reference.

A view does not own its target: it stays valid while the object it refers to exists at the same address, which the owner of that object — a local, a field or a container — decides; the Standard Library calls such a view borrowed.

Ref-like locals and fields follow the schema-specific rules in Selection and destinations.

Selection and destinations

Selection is the identification of a builtin, local, or field, by name or by ordinal, apart from the read, call, or write then performed on it; the form of the mention decides which. A builtin executes when selected by NAME. For a non-static, non-callable In-place local or field, NAME or .NAME pushes an immutable view of the object and @NAME or .@NAME a mutable view; for a field, the view is mutable only when the enclosing Dict is reached through a mutable path. A static, non-callable object (a virtual local or field, see Structs) is pushed as its value by both forms, never as a view. A static Ref local — always a known NIL, since a static local cannot hold a non-NIL reference — is pushed with its stored mutability by both forms; a static Ref field, static Text, and static callable object follow their rules below. A Ref or Text local or field: NAME pushes an immutable copy of the stored reference; @NAME pushes the stored reference with its own mutability, for a field only through a mutable path; a Text is always immutable. A callable object — a Block, a non-NIL Code, or a Dict with a CALL field — is called by NAME or .NAME and pushed by @NAME or .@NAME. A Code known to be NIL is invalid to call ("Local «x» is a NIL Code" / "Field «x» is a NIL Code"). A non-callable Meta object is pushed as itself by both forms. !NAME and .!NAME select a destination: the top object is written into the selected object instead of that object being read.

Known and unknown values

The value of an object is known when it is available at compile time and takes part in processing as data, and unknown when it exists only at run time; the schema is available in both cases, and a Struct may hold known and unknown fields at once. “A known object” abbreviates “an object whose value is known”. Known are literals, Meta objects, schema-level results, and results determined by known operands; unknown are the inputs and results of Code calls, results that depend on an unknown operand or exist only at run time (the storageAddress of run-time storage), and objects read through a reference whose target is unknown.

Stable and unstable objects

An object is stable while every write to it is traceable at compile time, and unstable otherwise. For In-place storage, a known value implies stability; an unstable object has no known value, and destabilizing it also forgets its value. A stable object may hold an unknown value, the normal state of data the program produces at run time. Stability decides whether knowledge can return: writing a known value into a stable object makes its value known again (s: 0 dynamic; 2 !s leaves s known), while an unstable object stays unknown whatever is written into it. Other schemas have schema-specific knownness and stability rules; for example, codeRef yields a NIL Code for which known? and stable? both report FALSE.

Data stack and name stack

The data stack is the last-in, first-out sequence of objects through which operations take their inputs and leave their outputs; the name stack is the last-in, first-out sequence of names awaiting binding, pushed by the Label node NAME:. The Variable node ; pops one name and one object and creates a local of that name, so a: b:;; binds b to the top object and a to the one below. def creates the local from an object and a known Text name on the data stack instead. The local is the bound object itself: when that object is a view, as after mentioning another local, the new local is a reference, not a copy. After x: 5; y: x;, writing 6 !x makes y read 6; y: x new; creates a copy.

Processing model

Processing is the evaluation of a module's source nodes, in order, at compile time, over a data stack of objects. An operation is performed at compile time whenever the schemas and known values of the operands determine the result; otherwise the operation is deferred to run time and its result is an object with an unknown value: when the result depends on an unknown value, and also when it exists only at run time, such as a storage address. An operator also folds with one known operand when that operand fixes the result: x 0 * folds to 0 and x 2147483647 > to FALSE for an Int32, while x 0 + does not fold; each builtin page states its cases. Control flow follows the same rule: if with a known condition processes one branch; with an unknown condition it processes both and merges their outputs, and an output holding the same known value in both branches stays known.

Call boundary and inline processing

A call boundary is a new scope around the processing of a Block: its locals disappear when the Block ends and its stack outputs are merged into the caller. call and if process a Block behind one. Inline processing is processing in the current scope, without a call boundary: locals created there stay visible and no output merging takes place; ucall processes a Block or a known Text inline, uif (whose condition must be known) the selected Block. loop repeats a body that leaves a Cond deciding whether to continue; iterations performed at compile time may change the carried stack, but repetition at run time requires the same stack shape (number of objects and their schemas), (A -- A Cond), on every iteration.

Names, lookup, and overloads

Lookup is the selection of a name's newest visible candidate, searching from newest to oldest: locals of the enclosing scopes, fields of the current Dict, names of loaded modules, builtins. In a calling mention (NAME, .NAME) a callable Dict candidate with a PRE field takes part only if its PRE yields the known TRUE; a known FALSE, or a PRE that fails, passes lookup on to older candidates, and a PRE yielding an unknown Cond is an error; read and write mentions take the newest candidate without consulting PRE. This is how the Standard Library dispatches one name over several schemas.

Modules

A module is one source file with its own global scope. "path" use loads a module at most once per program and imports its public names; "path.name" use imports one public name. Neither form imports overloads: they take part in same-name lookup once their module is loaded, by either form; a private overload cannot be named in "path.name" use. A module is first loaded at the global level of some module: a use behind a call boundary may name only modules loaded before. A module path is resolved relative to the importing module first, then through the configured search path. Module-level locals with run-time storage are globals: their values are unknown from creation; static and Meta ones keep their values.

Foreign functions

A foreign function is a function reached by its symbol through the C calling interface, bound with importFunction: the inputs Dict lists the parameters in C declaration order, and at the call site the first C parameter is the top object, so a call pushes the arguments in reverse: f(a, b, c) has the stack effect (c b a -- result). The same order applies inside the body of an exportFunction.