Loading the Glon runtime…
Glon is a small Rebol-family language that runs on a tiny frozen machine (S1), natively and as WebAssembly in the browser. This primer teaches enough Glon to read and change simple programs.
This primer is instructional. The normative rules are in GLON-ALPHA-LAWS.md; where the two differ, the laws win.
Every example here is real: the live primer runs each one through the actual Glon WebAssembly runtime, and the native test suite checks every result shown below. The result of an example is shown after =>: it is the value of the program's last expression. A result starting ** uncaught means the program was stopped by a raised SIN! that nothing caught.
Live, editable version: https://gchiu.github.io/rebol-substrate-experiment/shop/primer.html
A program is a sequence of values. Numbers are integers. A word names a value; a set-word (a word with a trailing colon) gives it one.
Glon calls are prefix: the function comes first and takes a fixed number of arguments. There is no operator precedence and there are no parentheses; each function simply takes the next complete expressions it needs. + 1 * 2 3 reads as + 1 (* 2 3).
A word with a leading quote ('tea) is a word used as data, not looked up.
Square brackets make a block. A block is just data (a list of values) until something evaluates it. do evaluates a block.
func [params] [body] makes a function. Calling it needs no parentheses or commas: the function takes as many following values as it has parameters.
Calls nest by arity: double double 5 is double (double 5).
either condition [then] [else] evaluates one of two blocks. Comparisons are prefix too. none and 0 are false; every other value is true (there are no true/false words; use 1 and 0).
case takes ONE block of pairs: a condition block followed by an action block. Conditions are tried in order; the first true one runs its action and case returns the action's result. [1] is a catch-all condition. With no match, case returns none. (case is ordinary Glon from case.glon, loaded as a library.)
A function remembers the context it was made in. A function returned from another function is a closure: it keeps its parameters alive.
A set-word inside a function updates the nearest existing binding of that word; if there is none, it creates a local of the current call.
The lexical fence. A plain block that refers to a function's locals depends on that one call. It may be used while the call is running, but it may not escape it. To take behaviour out of a call, make a closure.
Legal: the block [ + x 1 ] is evaluated inside the call that owns x.
Illegal: do [ [x] ] returns the inner block [x] out of f, but that block still refers to f's x. The runtime refuses, at the moment of escape, by raising a SIN! of type escape, id return. Its last field, shown here as <site>, is the block's internal lexical site id: an implementation number (the live page shows the real one).
The legal way to take x out is a closure (does makes a function with no parameters):
A SIN! is a first-class value describing a failure: a type, an id and an argument. A SIN! is inert data until it is raised: you can store it, pass it and inspect it freely.
raise starts propagation: it abandons the current work and unwinds to the nearest judge. judge block runs the block; if something inside raises, judge returns the SIN! as ordinary inert data.
Inspect a SIN! with sin?, sin-type, sin-id and sin-arg:
After judge returns, execution simply continues:
With no judge, a raised SIN! stops the program:
Runtime checks raise SIN!s too. The lexical fence above can be judged like any other raise:
Text in double quotes is a STRING!. Strings are immutable: string operations return new strings and never change their inputs. They have qualified names: s/+ joins two strings, s/= compares their contents, s/length counts their bytes and s/print prints one. (For now s/+ is simply one word; a / inside a word is reserved for qualified names. A bare / is still division.)
s/+ built a new string; s itself is unchanged:
s/length counts stored bytes (Glon has no Unicode character semantics yet):
s/= compares contents. = is identity, so two separately built strings with the same text are s/= but not =:
s/print "hello" writes the string's bytes and a newline to the output (print prints integers only). Giving a string operation something that is not a string raises a SIN! of type type:
Glon tasks are cooperative: spawn block creates a task, yield hands control to the next task, and run-tasks runs them until all finish. These words come from the task library shipped with the demos (demos/tuple-space.glon), loaded for the examples below. Two tasks interleave their digits:
A task body obeys the lexical fence: a block that depends on the spawning call is rejected with 'escape 'transport before the task is created.
Glon looks familiar, which is exactly why these differences matter:
| In Rebol / Red | In Glon today |
|---|---|
1 + 2 * 3 (infix) | prefix only: + 1 * 2 3; comparisons too: > n 0 |
( ... ) groups an expression | no parentheses; fixed arity decides grouping |
0 is true; true / false | 0 and none are false; no true / false words (use 1 / 0) |
case [cond [action] ...], case/all | case [ [cond] [action] ... ]: conditions are blocks; no /all |
foreach, each, while, repeat, loop | no loop words; use recursion |
switch | none; use case |
type? | none |
try, attempt, error!, catch / throw | SIN!, create-sin, raise, judge |
| an unset word is a catchable error | an unset word halts the run (a machine fail-stop, not a SIN!) |
func set-words are global (Rebol); function collects locals (Red) | a set-word updates the nearest existing binding, else makes a local |
| a bound block can travel anywhere | a block that uses a call's locals may not outlive that call; use a closure |
[1] = [1] is true | = is identity for blocks and strings (= [1] [1] is 0); str-eq compares strings |
| threads, ports | cooperative tasks: spawn, yield, run-tasks (library words) |
print shows any value | print shows integers only; s/print prints a string |
append changes a string in place | strings are immutable: s/+ returns a new string |