Trace a value — the period
The most distinctive move in everyday Axioma: end a line with a period and it traces the value — prints it, no function call at all. Both halves are inspired by older languages: printing a value with a dot by Forth (where ., "dot", pops and prints the top of the stack), and ending a statement with a dot by Prolog (where every clause closes with .). Axioma's . does both at once — it shows a value and terminates the statement. (A trace shows the value as the language sees it, so a string keeps its quotes; println is the more familiar way to write clean output for a person.)
Because the dot terminates a statement, several can share one line — and the playground traces the last line for you automatically, which is the ⇒ you'll see in every example. (A dot directly before a digit is still a decimal point, so 3.14 is a number, not a trace.)
Bindings — REBOL's set-word colon
Axioma binds a name to a value with a colon: name: value — no let, no var. The colon-binding is inspired by REBOL (Carl Sassenrath, 1997), where word: is an atomic set-word — the colon is fused onto the word itself. Axioma adopts that look but treats : as an ordinary binding operator (its own token), so even x : 5 — with a space — still binds. (REBOL itself drew on Lisp, Forth, Logo and Self; the set-word notation is its own — Algol and Smalltalk used :=, Logo used make.)
: creates a binding; = reassigns an existing one — and the difference is enforced. Use = on a name you never declared and Axioma stops you, telling you to declare it with : first. Comparison for equality is a third thing again: ==.
You can also bind several names at once, left-to-right.
Words, types & introspection
A subtlety worth naming: x: 56 doesn't declare a variable in the C sense — it binds a word. First-class words long predate REBOL: Forth is built entirely on words (its dictionary maps each word to code), and Logo treats the word as a primitive datatype — Logo even fetched a word's value with :x, the very get-word Axioma still uses. Axioma inherits that lineage through REBOL (and Lisp's first-class symbols sit underneath it). A bare word like x evaluates to its value; :x gets that value; 'x is the word itself, an unevaluated literal of type Word (the ' quote is Lisp's). The word is just a name in a binding table, so it can hold a value of any type.
That last clause — any type — matters: every value has a type. Ask for it with the @ sigil or the type function; the names match the built-in type-concepts, so @5, type(5) and 5 is Integer all agree. And @ reaches past values: aim it at a bare keyword or operator and it reports that token's category — @if is a control_flow_keyword, @+ an arithmetic_operator.
The everyday value types are Integer, Float, String, Boolean, Array, Set, Tuple and ObjectMap (a hash) — with many more for the reasoning machinery. You can also declare your own closed set of named values, an enum, with enumerates: each member is a real value you can order with .ord and count with len.
The language also documents itself. Write doc followed by any word — doc why, doc map — and Axioma prints its built-in reference for that word, in a script or the REPL.
Your own words — aliases
If words are first-class, the natural next step is to add your own. alias new = original gives any keyword, builtin, or word a second name — so the surface can read the way you think, or skin a small domain language. It's the focused version of a tradition Lisp and Forth are built on — programs that grow the language by defining new words. Here an alias is a synonym, woven in as the source is read, so define it before you use it.
An alias is additive — it adds a fresh word; it can't shadow a word that already exists, so the new name must be unused (ask for alias repeat = while and Axioma tells you repeat is already taken). And aliases aren't only for keywords — aim one at a builtin, or at a function you wrote. Drop it again with unalias name.
Where : binds now, declare binds lazily — the expression is captured and only computed on first use, so it can even refer to names defined later.
Blocks — the versatile [ ]
Square brackets are the most versatile punctuation in Axioma. At heart, [ ] is a block — a sequence of steps whose value is its last expression. That single idea powers a remarkable range of the language:
- the body of a function —
func(x) [ … ](next lesson); - the body of control flow —
if … then [ … ] else [ … ],while … [ … ],for … in … [ … ]; - a value-block you drop inline to compute something in a few steps (just below);
- a list comprehension —
[ x * x | x <- xs ](Lesson 6); - an array literal, when the brackets hold a comma-separated series of values —
[1, 2, 3]; - and the language & lens forms —
[python | … ],[sql | … ],[model | … ]— that run or translate another notation.
How does Axioma tell an array from a block? By context and content. In a body — a function or a control-flow branch — the brackets are always a block, yielding the last value (so if … then ["big"] else ["small"] gives the string "big", not a one-element array). Standing on their own, brackets of bare values are an array, while brackets that contain statements — like the bindings below — are a value-block.
That same block is the body of control flow — a loop runs a block per iteration, and an if (an expression) hands back the value of whichever branch it takes:
Functions
A function is just a value — you bind it with : like anything else, and its body is a [ ] block. Call it with parentheses.
There's an optional named form, and a lightweight lambda … => for short functions:
Functions can call themselves (recursion), and can return other functions that capture their surrounding scope (closures):
Functions and operators are interchangeable values, so the same arithmetic reads three ways: infix is the default, prefix treats any operator as a function you can pass around, and a Forth/Pop-11 stack gives postfix / RPN-style computation.
Composition — functions over collections
Functions are first-class, so you pass them around. map applies one to every item; filter keeps what matches; reduce folds a collection to a single value.
And because a function is a value, you can compose small ones into a pipeline:
Sets, arrays & comprehensions
Sets and comprehensions sit at the centre of Axioma — a great deal of the language reads like the set-builder notation from a maths text, and runs. But it all rests on one distinction worth getting straight first: the difference between an array and a set.
Arrays and sets — order vs uniqueness
An array [ ] is an ordered sequence: it keeps elements in the order you wrote them, keeps duplicates, and you reach one by position — Axioma indexes from 1. A set { } is an unordered collection of unique values: duplicates collapse, there are no positions to index, and because a set has no intrinsic order Axioma may print its members in any order — so never rely on it. You don't ask a set “what's at position 1?”; you ask “is this in it?”.
Ranges — generate a sequence
You rarely write a long sequence by hand. A range generates one with the .. operator, inclusive of both ends — and the bracket you choose decides the kind: [1..6] is an ordered Array, while the bare 1..6 is a Set. Add a step with a third segment ([0..10..2]), count downward by giving a larger start ([5..1]), and range over characters as well as integers. The range(...) function is the same generator in call form.
Ranges are the natural source for everything in this lesson — feed one to a comprehension or a collection function and the sequence streams straight in:
The algebra of sets
Because a set is a mathematical object, the whole algebra is built in: membership ∈ and its negation ∉, union ∪, intersection ∩, difference \, symmetric difference △ (the members in exactly one set — ∆ and ⊖ work too), cardinality with len, and an equality that ignores order. The subset relations come in two strengths: ⊆ / ⊇ allow equality, while ⊂ / ⊃ are proper — strict, and so false when the two sets are equal. If your keyboard lacks the glyphs, most have a word form too — in, union, intersect, difference, symdiff.
Can't type the glyph? Ask Axioma
Since these symbols are hard to type, the language carries a catalog of its own glyphs. symbols() lists them (optionally by category), and glyph(name) fetches one by its Axioma name, its LaTeX name, a Greek letter, an emoji name, or a raw U+… codepoint — so you never need the special key. Each result is a first-class Glyph that prints as the character, compares equal to its string, and carries .name / .meaning / .codepoint. (In the REPL, :symbols prints the table and :doc ∪ explains one.)
Comprehensions — set-builder notation
A comprehension builds a collection by describing it. Read { x | x <- xs, cond } exactly as the maths: “the set of x drawn from xs such that cond.” The part before the bar can transform each element; every comma-separated condition after it must hold — they combine with and. (mod is the word form of %.)
List comprehensions — same notation, ordered result
Swap the braces for brackets and the same notation yields an array — so the result keeps its order and its duplicates. Either form can draw from more than one source at once; several generators produce every combination (a Cartesian product).
Python's spelling, and dict comprehensions
Axioma is a superset of Python's comprehension syntax, so the pipe-less for … if … form pastes in unchanged. And a comprehension whose head is a key: value pair builds a hash (a dict) instead of a set.
Query clauses, and lazy streams
Comprehensions also carry the SQL-style clauses orderby, limit and offset for shaping a result. And wrapping a comprehension in parentheses makes it lazy — a stream computed on demand, so you can pull a handful of values from a source of a million without ever building the whole thing.
Quantifiers — logic that evaluates
The universal ∀ and existential ∃ quantifiers range over a set and evaluate to a truth value. Write them as words…
…or as the symbols mathematicians actually write — the same AST either way:
Logic & rules
Declare a relation, assert some facts, and write rules with <== (“holds when”). Rules can be recursive — here the engine computes the full transitive closure of a graph, Datalog-style, iterating to a fixpoint.
Rules come in a 2×2: strict (<== / ==>) vs defeasible (<~~ / ~~>), and backward (head <op> body) vs forward (body <op> head). Strict rules derive theorems; defeasible ones hold by default and derive conjectures — and the grade is recorded on every conclusion.
Abduction runs inference backwards: given something observed, abduce proposes the best explanation a rule could supply — Peirce's "inference to the best explanation," built in (the cognitive kernel also has understand and examine).
Concepts & the copula is
A concept starts as a class but is more than one — it can also carry actions, a defining boundary, and an epistemic grounding (the overview goes deeper). The everyday parts: declare fields with has (several at once), make an instance with the indefinite article a / an, and classify with Russell's copula is — which Axioma keeps precise: membership (∈) for an instance, class-inclusion (⊆) between two concepts.
Subclass with extends, and give a concept behaviour with an action — a method whose body sees the receiving instance as it. Actions are inherited down the extends chain and can be overridden.
Beyond true and false
Real knowledge is sometimes missing and sometimes contradictory. Axioma builds that in: Belnap's four-valued logic has a value for “asserted and denied” (⊤⊥), and Kleene's three-valued logic handles the unknown (om) without collapsing.
Belnap's four values form a bilattice — ordered two ways at once: by truth (false → true) and by information (neither → both). Contradiction (⊤⊥) doesn't crash; it propagates through the operators.
Truth is only one axis. A single fact carries orthogonal axes at once — its Belnap truth, its epistemic grounding (Lesson 8), and its kind (empirical, logical, …) — each set and queried independently.
Refinements — one word, adapted
A refinement is a /word suffix that adapts a keyword or operator instead of inventing a new one — REBOL's idea, carried into Axioma's meaning. The copula is the clearest case: bare is tests class membership (∈), while is/same tests identity (= — the very same entity, not just an equal one).
The same /word dialect tunes a fact's lifecycle: /persist writes it to the knowledge base, /transient keeps it to this session. And it composes with epistemic grade — axiom/transient is still graded axiom, just not saved to disk.
Code is data — metaprogramming
Axioma is homoiconic: programs are data you can build, inspect and run. parse turns a string into an AST, quote captures code unevaluated, do runs an AST, and a macro rewrites code before it executes.
The *form family renders any expression for inspection — fullform as a Mathematica-style string, treeform as a tree, and tableform a relation's extent as a table.
A binary relation is also a graph: graphform renders it as ASCII, as a Sowa conceptual graph, or as Graphviz DOT you can paste into any renderer.
Meaning & natural language
Axioma can model meaning directly. A cognitive word carries Wierzbicka's NSM semantic primes and a Schank Conceptual-Dependency act — the building blocks linguists use to decompose meaning.
Relations can label each place with a semantic role — Lojban's selbri place structure, lifted into the language. Natural-language dialects like <<Lojban| … >> and cross-language translate build on the same idea (translation needs an AI provider, so it runs in the desktop CLI rather than this in-browser sandbox).
Pattern matching
ML- and Rust-style match … with tries each arm top-to-bottom. A when clause adds a guard; _ catches everything else. match is an expression — it yields the value of the first arm that fits.
match is total by construction: when nothing matches and there is no _, it returns om (the undefined value) rather than crashing — so a partial match stays recoverable.
Errors are values
Axioma has no exceptions and no stack unwinding. try(expr) runs the expression and hands back any fault as an ordinary, inspectable value — which auto-classifies into a concept like DivByZero, TypeError or IndexError, so you can branch on it with the same is copula you use everywhere else.
otherwise is the railway operator: if the left side faults, take the right instead.
Strings & text
${…} interpolates any expression straight into a string — names, arithmetic, calls.
\u{…} escapes reach the entire Unicode range — handy for the mathematical symbols Axioma reads natively — and chr/ord convert between a character and its codepoint.
Stacks & loops
Axioma carries a Forth / Pop-11 heritage: a first-class stack with push, pop, peek, dup, swap and depth.
And when a loop is the clearest tool, the imperative forms are there: while, repeat … until, and foreach.
That's the core.
You've met bindings, blocks, functions, sets, logic, concepts, many-valued truth, refinements, metaprogramming and natural-language meaning — enough to read most Axioma. Take any example into the full playground and keep going.
The full Axioma Manual and textbook are coming to the site — this tutorial is the slimmed-down tour.