How a program runs
You have written programs now. This page says what happens to one, in enough detail to predict what the language will do and no more.
- source textwhat you wrote
- lexthe language's symbols
- tokenswords and symbols, no meaning yet
- parse
- raw programbindings and outputs, untyped
- composethe language and the port layers
- raw program + descriptorand everything it may use
- analyse
- core programtyped, checked, pruned
- evaluatethe input values
- valuesone per output
Five steps. Each takes what is on its left and produces what is on its right, and none of them can reach backwards.
Lex: text becomes tokens
Section titled “Lex: text becomes tokens”The lexer cuts your text into tokens: names, numbers, strings and symbols. It knows which symbols
the language has, so >= comes out as one token rather than two characters, and that is
all it knows. A word is a word to it, whether it is let, If or a name of yours.
What you can rely on: a string with no closing quote, or a character the language has no use for, is reported here, with a position.
Parse: tokens become a program
Section titled “Parse: tokens become a program”The parser turns the tokens into bindings and outputs. This step knows the shape of the language
and nothing else: it does not know which ops exist, which types exist, or what your application
declared. It does rewrite every symbol into the op it stands for as it reads, so
$price * $quantity is already Multiply($price, $quantity) when it leaves.
That is why $n is an input here purely because of the $ (no table is consulted), and
why a misspelled op name gets through this step without complaint. What you can rely on: a
syntax error is reported here, with a position, and nothing further happens.
Compose: what the program may use
Section titled “Compose: what the program may use”Checking a program needs a list of everything it may use: the ops and types of the language, and the inputs and outputs your application declared. That list is not fixed by the language. It arrives in layers, one from the language and more from your application, and composing them into one is this step.
It reads what was declared, never what you wrote, so a clash between declarations (two layers claiming the same name) is reported here, before your code is looked at. Ports and layers is where the layers are explained.
Analyse: the program is checked
Section titled “Analyse: the program is checked”This is where almost everything happens. The analyser walks the bindings in dependency order and works out the type of each one, and it checks every op call against what that op actually takes.
It also decides what is worth keeping. A binding no output can reach is dropped. An output whose working failed is dropped, and if your application had marked that output required, the whole program fails to load; if not, the rest carries on without it.
Two things to rely on. Errors are collected, not thrown at the first one, so you get the full list rather than peeling them off one at a time. And soundness is per output: one broken result does not stop the others, which is why a half-finished program keeps producing the parts that work.
Evaluate: values, on demand
Section titled “Evaluate: values, on demand”The evaluator does not run your program top to bottom. Outputs pull: asking for an output walks only the nodes that output depends on, and each node remembers its last value.
So when an input changes, the recompute covers exactly the nodes that read it, directly or
through other bindings. Everything else keeps what it had. You saw this on
Inputs: move $price and the shipping calculation is not touched.
Two things to rely on. A binding is computed at most once per evaluation however many names read it. And the last good program keeps running: while your text does not compile, the values on screen are the ones from the program that did, marked stale rather than hidden, so whatever is acting on them has something to act on.
What each step may not do
Section titled “What each step may not do”Worth stating as plainly as the rest, because it is what makes the chain predictable:
- Lexing may not decide what a word means.
letandIfare both just words. - Parsing may not resolve a name or infer a type. It does not know what exists.
- Composing may not read your code. It sees what was declared, never what was written.
- Analysis may not compute a value. It works with types, not numbers.
- Evaluation may not report a type error. Everything checkable was checked already; what reaches it is a runtime failure, and it arrives on the outputs rather than as a diagnostic.
That is Learn
Section titled “That is Learn”You can write programs and predict what they do. Two ways on from here:
- The standard library: every op, with its signature and a worked example, generated from the language itself.
- How it works: the same chain again, one page per step, with the reasons. Learn told you what you can rely on; that section tells you why, and in what order.
And if you want the language inside an application of your own, Host developers starts there.